##// END OF EJS Templates
Merge additional fixes for my matcher changes
Alexis S. L. Carvalho -
r4256:fe0fe0b4 merge default
parent child Browse files
Show More
@@ -1,3340 +1,3342 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
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 demandimport; demandimport.enable()
8 import demandimport; demandimport.enable()
9 from node import *
9 from node import *
10 from i18n import _
10 from i18n import _
11 import bisect, os, re, sys, signal, imp, urllib, pdb, shlex, stat
11 import bisect, os, re, sys, signal, imp, urllib, pdb, shlex, stat
12 import fancyopts, ui, hg, util, lock, revlog, bundlerepo
12 import fancyopts, ui, hg, util, lock, revlog, bundlerepo
13 import difflib, patch, time, help, mdiff, tempfile
13 import difflib, patch, time, help, mdiff, tempfile
14 import traceback, errno, version, atexit, socket
14 import traceback, errno, version, atexit, socket
15 import archival, changegroup, cmdutil, hgweb.server, sshserver
15 import archival, changegroup, cmdutil, hgweb.server, sshserver
16
16
17 class UnknownCommand(Exception):
17 class UnknownCommand(Exception):
18 """Exception raised if command is not in the command table."""
18 """Exception raised if command is not in the command table."""
19 class AmbiguousCommand(Exception):
19 class AmbiguousCommand(Exception):
20 """Exception raised if command shortcut matches more than one command."""
20 """Exception raised if command shortcut matches more than one command."""
21
21
22 def bail_if_changed(repo):
22 def bail_if_changed(repo):
23 modified, added, removed, deleted = repo.status()[:4]
23 modified, added, removed, deleted = repo.status()[:4]
24 if modified or added or removed or deleted:
24 if modified or added or removed or deleted:
25 raise util.Abort(_("outstanding uncommitted changes"))
25 raise util.Abort(_("outstanding uncommitted changes"))
26
26
27 def logmessage(opts):
27 def logmessage(opts):
28 """ get the log message according to -m and -l option """
28 """ get the log message according to -m and -l option """
29 message = opts['message']
29 message = opts['message']
30 logfile = opts['logfile']
30 logfile = opts['logfile']
31
31
32 if message and logfile:
32 if message and logfile:
33 raise util.Abort(_('options --message and --logfile are mutually '
33 raise util.Abort(_('options --message and --logfile are mutually '
34 'exclusive'))
34 'exclusive'))
35 if not message and logfile:
35 if not message and logfile:
36 try:
36 try:
37 if logfile == '-':
37 if logfile == '-':
38 message = sys.stdin.read()
38 message = sys.stdin.read()
39 else:
39 else:
40 message = open(logfile).read()
40 message = open(logfile).read()
41 except IOError, inst:
41 except IOError, inst:
42 raise util.Abort(_("can't read commit message '%s': %s") %
42 raise util.Abort(_("can't read commit message '%s': %s") %
43 (logfile, inst.strerror))
43 (logfile, inst.strerror))
44 return message
44 return message
45
45
46 def setremoteconfig(ui, opts):
46 def setremoteconfig(ui, opts):
47 "copy remote options to ui tree"
47 "copy remote options to ui tree"
48 if opts.get('ssh'):
48 if opts.get('ssh'):
49 ui.setconfig("ui", "ssh", opts['ssh'])
49 ui.setconfig("ui", "ssh", opts['ssh'])
50 if opts.get('remotecmd'):
50 if opts.get('remotecmd'):
51 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
51 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
52
52
53 # Commands start here, listed alphabetically
53 # Commands start here, listed alphabetically
54
54
55 def add(ui, repo, *pats, **opts):
55 def add(ui, repo, *pats, **opts):
56 """add the specified files on the next commit
56 """add the specified files on the next commit
57
57
58 Schedule files to be version controlled and added to the repository.
58 Schedule files to be version controlled and added to the repository.
59
59
60 The files will be added to the repository at the next commit. To
60 The files will be added to the repository at the next commit. To
61 undo an add before that, see hg revert.
61 undo an add before that, see hg revert.
62
62
63 If no names are given, add all files in the repository.
63 If no names are given, add all files in the repository.
64 """
64 """
65
65
66 names = []
66 names = []
67 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
67 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
68 if exact:
68 if exact:
69 if ui.verbose:
69 if ui.verbose:
70 ui.status(_('adding %s\n') % rel)
70 ui.status(_('adding %s\n') % rel)
71 names.append(abs)
71 names.append(abs)
72 elif repo.dirstate.state(abs) == '?':
72 elif repo.dirstate.state(abs) == '?':
73 ui.status(_('adding %s\n') % rel)
73 ui.status(_('adding %s\n') % rel)
74 names.append(abs)
74 names.append(abs)
75 if not opts.get('dry_run'):
75 if not opts.get('dry_run'):
76 repo.add(names)
76 repo.add(names)
77
77
78 def addremove(ui, repo, *pats, **opts):
78 def addremove(ui, repo, *pats, **opts):
79 """add all new files, delete all missing files
79 """add all new files, delete all missing files
80
80
81 Add all new files and remove all missing files from the repository.
81 Add all new files and remove all missing files from the repository.
82
82
83 New files are ignored if they match any of the patterns in .hgignore. As
83 New files are ignored if they match any of the patterns in .hgignore. As
84 with add, these changes take effect at the next commit.
84 with add, these changes take effect at the next commit.
85
85
86 Use the -s option to detect renamed files. With a parameter > 0,
86 Use the -s option to detect renamed files. With a parameter > 0,
87 this compares every removed file with every added file and records
87 this compares every removed file with every added file and records
88 those similar enough as renames. This option takes a percentage
88 those similar enough as renames. This option takes a percentage
89 between 0 (disabled) and 100 (files must be identical) as its
89 between 0 (disabled) and 100 (files must be identical) as its
90 parameter. Detecting renamed files this way can be expensive.
90 parameter. Detecting renamed files this way can be expensive.
91 """
91 """
92 sim = float(opts.get('similarity') or 0)
92 sim = float(opts.get('similarity') or 0)
93 if sim < 0 or sim > 100:
93 if sim < 0 or sim > 100:
94 raise util.Abort(_('similarity must be between 0 and 100'))
94 raise util.Abort(_('similarity must be between 0 and 100'))
95 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
95 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
96
96
97 def annotate(ui, repo, *pats, **opts):
97 def annotate(ui, repo, *pats, **opts):
98 """show changeset information per file line
98 """show changeset information per file line
99
99
100 List changes in files, showing the revision id responsible for each line
100 List changes in files, showing the revision id responsible for each line
101
101
102 This command is useful to discover who did a change or when a change took
102 This command is useful to discover who did a change or when a change took
103 place.
103 place.
104
104
105 Without the -a option, annotate will avoid processing files it
105 Without the -a option, annotate will avoid processing files it
106 detects as binary. With -a, annotate will generate an annotation
106 detects as binary. With -a, annotate will generate an annotation
107 anyway, probably with undesirable results.
107 anyway, probably with undesirable results.
108 """
108 """
109 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
109 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
110
110
111 if not pats:
111 if not pats:
112 raise util.Abort(_('at least one file name or pattern required'))
112 raise util.Abort(_('at least one file name or pattern required'))
113
113
114 opmap = [['user', lambda x: ui.shortuser(x.user())],
114 opmap = [['user', lambda x: ui.shortuser(x.user())],
115 ['number', lambda x: str(x.rev())],
115 ['number', lambda x: str(x.rev())],
116 ['changeset', lambda x: short(x.node())],
116 ['changeset', lambda x: short(x.node())],
117 ['date', getdate], ['follow', lambda x: x.path()]]
117 ['date', getdate], ['follow', lambda x: x.path()]]
118 if (not opts['user'] and not opts['changeset'] and not opts['date']
118 if (not opts['user'] and not opts['changeset'] and not opts['date']
119 and not opts['follow']):
119 and not opts['follow']):
120 opts['number'] = 1
120 opts['number'] = 1
121
121
122 ctx = repo.changectx(opts['rev'])
122 ctx = repo.changectx(opts['rev'])
123
123
124 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
124 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
125 node=ctx.node()):
125 node=ctx.node()):
126 fctx = ctx.filectx(abs)
126 fctx = ctx.filectx(abs)
127 if not opts['text'] and util.binary(fctx.data()):
127 if not opts['text'] and util.binary(fctx.data()):
128 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
128 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
129 continue
129 continue
130
130
131 lines = fctx.annotate(follow=opts.get('follow'))
131 lines = fctx.annotate(follow=opts.get('follow'))
132 pieces = []
132 pieces = []
133
133
134 for o, f in opmap:
134 for o, f in opmap:
135 if opts[o]:
135 if opts[o]:
136 l = [f(n) for n, dummy in lines]
136 l = [f(n) for n, dummy in lines]
137 if l:
137 if l:
138 m = max(map(len, l))
138 m = max(map(len, l))
139 pieces.append(["%*s" % (m, x) for x in l])
139 pieces.append(["%*s" % (m, x) for x in l])
140
140
141 if pieces:
141 if pieces:
142 for p, l in zip(zip(*pieces), lines):
142 for p, l in zip(zip(*pieces), lines):
143 ui.write("%s: %s" % (" ".join(p), l[1]))
143 ui.write("%s: %s" % (" ".join(p), l[1]))
144
144
145 def archive(ui, repo, dest, **opts):
145 def archive(ui, repo, dest, **opts):
146 '''create unversioned archive of a repository revision
146 '''create unversioned archive of a repository revision
147
147
148 By default, the revision used is the parent of the working
148 By default, the revision used is the parent of the working
149 directory; use "-r" to specify a different revision.
149 directory; use "-r" to specify a different revision.
150
150
151 To specify the type of archive to create, use "-t". Valid
151 To specify the type of archive to create, use "-t". Valid
152 types are:
152 types are:
153
153
154 "files" (default): a directory full of files
154 "files" (default): a directory full of files
155 "tar": tar archive, uncompressed
155 "tar": tar archive, uncompressed
156 "tbz2": tar archive, compressed using bzip2
156 "tbz2": tar archive, compressed using bzip2
157 "tgz": tar archive, compressed using gzip
157 "tgz": tar archive, compressed using gzip
158 "uzip": zip archive, uncompressed
158 "uzip": zip archive, uncompressed
159 "zip": zip archive, compressed using deflate
159 "zip": zip archive, compressed using deflate
160
160
161 The exact name of the destination archive or directory is given
161 The exact name of the destination archive or directory is given
162 using a format string; see "hg help export" for details.
162 using a format string; see "hg help export" for details.
163
163
164 Each member added to an archive file has a directory prefix
164 Each member added to an archive file has a directory prefix
165 prepended. Use "-p" to specify a format string for the prefix.
165 prepended. Use "-p" to specify a format string for the prefix.
166 The default is the basename of the archive, with suffixes removed.
166 The default is the basename of the archive, with suffixes removed.
167 '''
167 '''
168
168
169 node = repo.changectx(opts['rev']).node()
169 node = repo.changectx(opts['rev']).node()
170 dest = cmdutil.make_filename(repo, dest, node)
170 dest = cmdutil.make_filename(repo, dest, node)
171 if os.path.realpath(dest) == repo.root:
171 if os.path.realpath(dest) == repo.root:
172 raise util.Abort(_('repository root cannot be destination'))
172 raise util.Abort(_('repository root cannot be destination'))
173 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
173 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
174 kind = opts.get('type') or 'files'
174 kind = opts.get('type') or 'files'
175 prefix = opts['prefix']
175 prefix = opts['prefix']
176 if dest == '-':
176 if dest == '-':
177 if kind == 'files':
177 if kind == 'files':
178 raise util.Abort(_('cannot archive plain files to stdout'))
178 raise util.Abort(_('cannot archive plain files to stdout'))
179 dest = sys.stdout
179 dest = sys.stdout
180 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
180 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
181 prefix = cmdutil.make_filename(repo, prefix, node)
181 prefix = cmdutil.make_filename(repo, prefix, node)
182 archival.archive(repo, dest, node, kind, not opts['no_decode'],
182 archival.archive(repo, dest, node, kind, not opts['no_decode'],
183 matchfn, prefix)
183 matchfn, prefix)
184
184
185 def backout(ui, repo, rev, **opts):
185 def backout(ui, repo, rev, **opts):
186 '''reverse effect of earlier changeset
186 '''reverse effect of earlier changeset
187
187
188 Commit the backed out changes as a new changeset. The new
188 Commit the backed out changes as a new changeset. The new
189 changeset is a child of the backed out changeset.
189 changeset is a child of the backed out changeset.
190
190
191 If you back out a changeset other than the tip, a new head is
191 If you back out a changeset other than the tip, a new head is
192 created. This head is the parent of the working directory. If
192 created. This head is the parent of the working directory. If
193 you back out an old changeset, your working directory will appear
193 you back out an old changeset, your working directory will appear
194 old after the backout. You should merge the backout changeset
194 old after the backout. You should merge the backout changeset
195 with another head.
195 with another head.
196
196
197 The --merge option remembers the parent of the working directory
197 The --merge option remembers the parent of the working directory
198 before starting the backout, then merges the new head with that
198 before starting the backout, then merges the new head with that
199 changeset afterwards. This saves you from doing the merge by
199 changeset afterwards. This saves you from doing the merge by
200 hand. The result of this merge is not committed, as for a normal
200 hand. The result of this merge is not committed, as for a normal
201 merge.'''
201 merge.'''
202
202
203 bail_if_changed(repo)
203 bail_if_changed(repo)
204 op1, op2 = repo.dirstate.parents()
204 op1, op2 = repo.dirstate.parents()
205 if op2 != nullid:
205 if op2 != nullid:
206 raise util.Abort(_('outstanding uncommitted merge'))
206 raise util.Abort(_('outstanding uncommitted merge'))
207 node = repo.lookup(rev)
207 node = repo.lookup(rev)
208 p1, p2 = repo.changelog.parents(node)
208 p1, p2 = repo.changelog.parents(node)
209 if p1 == nullid:
209 if p1 == nullid:
210 raise util.Abort(_('cannot back out a change with no parents'))
210 raise util.Abort(_('cannot back out a change with no parents'))
211 if p2 != nullid:
211 if p2 != nullid:
212 if not opts['parent']:
212 if not opts['parent']:
213 raise util.Abort(_('cannot back out a merge changeset without '
213 raise util.Abort(_('cannot back out a merge changeset without '
214 '--parent'))
214 '--parent'))
215 p = repo.lookup(opts['parent'])
215 p = repo.lookup(opts['parent'])
216 if p not in (p1, p2):
216 if p not in (p1, p2):
217 raise util.Abort(_('%s is not a parent of %s') %
217 raise util.Abort(_('%s is not a parent of %s') %
218 (short(p), short(node)))
218 (short(p), short(node)))
219 parent = p
219 parent = p
220 else:
220 else:
221 if opts['parent']:
221 if opts['parent']:
222 raise util.Abort(_('cannot use --parent on non-merge changeset'))
222 raise util.Abort(_('cannot use --parent on non-merge changeset'))
223 parent = p1
223 parent = p1
224 hg.clean(repo, node, show_stats=False)
224 hg.clean(repo, node, show_stats=False)
225 revert_opts = opts.copy()
225 revert_opts = opts.copy()
226 revert_opts['date'] = None
226 revert_opts['date'] = None
227 revert_opts['all'] = True
227 revert_opts['all'] = True
228 revert_opts['rev'] = hex(parent)
228 revert_opts['rev'] = hex(parent)
229 revert(ui, repo, **revert_opts)
229 revert(ui, repo, **revert_opts)
230 commit_opts = opts.copy()
230 commit_opts = opts.copy()
231 commit_opts['addremove'] = False
231 commit_opts['addremove'] = False
232 if not commit_opts['message'] and not commit_opts['logfile']:
232 if not commit_opts['message'] and not commit_opts['logfile']:
233 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
233 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
234 commit_opts['force_editor'] = True
234 commit_opts['force_editor'] = True
235 commit(ui, repo, **commit_opts)
235 commit(ui, repo, **commit_opts)
236 def nice(node):
236 def nice(node):
237 return '%d:%s' % (repo.changelog.rev(node), short(node))
237 return '%d:%s' % (repo.changelog.rev(node), short(node))
238 ui.status(_('changeset %s backs out changeset %s\n') %
238 ui.status(_('changeset %s backs out changeset %s\n') %
239 (nice(repo.changelog.tip()), nice(node)))
239 (nice(repo.changelog.tip()), nice(node)))
240 if op1 != node:
240 if op1 != node:
241 if opts['merge']:
241 if opts['merge']:
242 ui.status(_('merging with changeset %s\n') % nice(op1))
242 ui.status(_('merging with changeset %s\n') % nice(op1))
243 hg.merge(repo, hex(op1))
243 hg.merge(repo, hex(op1))
244 else:
244 else:
245 ui.status(_('the backout changeset is a new head - '
245 ui.status(_('the backout changeset is a new head - '
246 'do not forget to merge\n'))
246 'do not forget to merge\n'))
247 ui.status(_('(use "backout --merge" '
247 ui.status(_('(use "backout --merge" '
248 'if you want to auto-merge)\n'))
248 'if you want to auto-merge)\n'))
249
249
250 def branch(ui, repo, label=None, **opts):
250 def branch(ui, repo, label=None, **opts):
251 """set or show the current branch name
251 """set or show the current branch name
252
252
253 With <name>, set the current branch name. Otherwise, show the
253 With <name>, set the current branch name. Otherwise, show the
254 current branch name.
254 current branch name.
255
255
256 Unless --force is specified, branch will not let you set a
256 Unless --force is specified, branch will not let you set a
257 branch name that shadows an existing branch.
257 branch name that shadows an existing branch.
258 """
258 """
259
259
260 if label:
260 if label:
261 if not opts.get('force') and label in repo.branchtags():
261 if not opts.get('force') and label in repo.branchtags():
262 if label not in [p.branch() for p in repo.workingctx().parents()]:
262 if label not in [p.branch() for p in repo.workingctx().parents()]:
263 raise util.Abort(_('a branch of the same name already exists'
263 raise util.Abort(_('a branch of the same name already exists'
264 ' (use --force to override)'))
264 ' (use --force to override)'))
265 repo.dirstate.setbranch(util.fromlocal(label))
265 repo.dirstate.setbranch(util.fromlocal(label))
266 else:
266 else:
267 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
267 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
268
268
269 def branches(ui, repo):
269 def branches(ui, repo):
270 """list repository named branches
270 """list repository named branches
271
271
272 List the repository's named branches.
272 List the repository's named branches.
273 """
273 """
274 b = repo.branchtags()
274 b = repo.branchtags()
275 l = [(-repo.changelog.rev(n), n, t) for t, n in b.items()]
275 l = [(-repo.changelog.rev(n), n, t) for t, n in b.items()]
276 l.sort()
276 l.sort()
277 for r, n, t in l:
277 for r, n, t in l:
278 hexfunc = ui.debugflag and hex or short
278 hexfunc = ui.debugflag and hex or short
279 if ui.quiet:
279 if ui.quiet:
280 ui.write("%s\n" % t)
280 ui.write("%s\n" % t)
281 else:
281 else:
282 spaces = " " * (30 - util.locallen(t))
282 spaces = " " * (30 - util.locallen(t))
283 ui.write("%s%s %s:%s\n" % (t, spaces, -r, hexfunc(n)))
283 ui.write("%s%s %s:%s\n" % (t, spaces, -r, hexfunc(n)))
284
284
285 def bundle(ui, repo, fname, dest=None, **opts):
285 def bundle(ui, repo, fname, dest=None, **opts):
286 """create a changegroup file
286 """create a changegroup file
287
287
288 Generate a compressed changegroup file collecting changesets not
288 Generate a compressed changegroup file collecting changesets not
289 found in the other repository.
289 found in the other repository.
290
290
291 If no destination repository is specified the destination is assumed
291 If no destination repository is specified the destination is assumed
292 to have all the nodes specified by one or more --base parameters.
292 to have all the nodes specified by one or more --base parameters.
293
293
294 The bundle file can then be transferred using conventional means and
294 The bundle file can then be transferred using conventional means and
295 applied to another repository with the unbundle or pull command.
295 applied to another repository with the unbundle or pull command.
296 This is useful when direct push and pull are not available or when
296 This is useful when direct push and pull are not available or when
297 exporting an entire repository is undesirable.
297 exporting an entire repository is undesirable.
298
298
299 Applying bundles preserves all changeset contents including
299 Applying bundles preserves all changeset contents including
300 permissions, copy/rename information, and revision history.
300 permissions, copy/rename information, and revision history.
301 """
301 """
302 revs = opts.get('rev') or None
302 revs = opts.get('rev') or None
303 if revs:
303 if revs:
304 revs = [repo.lookup(rev) for rev in revs]
304 revs = [repo.lookup(rev) for rev in revs]
305 base = opts.get('base')
305 base = opts.get('base')
306 if base:
306 if base:
307 if dest:
307 if dest:
308 raise util.Abort(_("--base is incompatible with specifiying "
308 raise util.Abort(_("--base is incompatible with specifiying "
309 "a destination"))
309 "a destination"))
310 base = [repo.lookup(rev) for rev in base]
310 base = [repo.lookup(rev) for rev in base]
311 # create the right base
311 # create the right base
312 # XXX: nodesbetween / changegroup* should be "fixed" instead
312 # XXX: nodesbetween / changegroup* should be "fixed" instead
313 o = []
313 o = []
314 has = {nullid: None}
314 has = {nullid: None}
315 for n in base:
315 for n in base:
316 has.update(repo.changelog.reachable(n))
316 has.update(repo.changelog.reachable(n))
317 if revs:
317 if revs:
318 visit = list(revs)
318 visit = list(revs)
319 else:
319 else:
320 visit = repo.changelog.heads()
320 visit = repo.changelog.heads()
321 seen = {}
321 seen = {}
322 while visit:
322 while visit:
323 n = visit.pop(0)
323 n = visit.pop(0)
324 parents = [p for p in repo.changelog.parents(n) if p not in has]
324 parents = [p for p in repo.changelog.parents(n) if p not in has]
325 if len(parents) == 0:
325 if len(parents) == 0:
326 o.insert(0, n)
326 o.insert(0, n)
327 else:
327 else:
328 for p in parents:
328 for p in parents:
329 if p not in seen:
329 if p not in seen:
330 seen[p] = 1
330 seen[p] = 1
331 visit.append(p)
331 visit.append(p)
332 else:
332 else:
333 setremoteconfig(ui, opts)
333 setremoteconfig(ui, opts)
334 dest = ui.expandpath(dest or 'default-push', dest or 'default')
334 dest = ui.expandpath(dest or 'default-push', dest or 'default')
335 other = hg.repository(ui, dest)
335 other = hg.repository(ui, dest)
336 o = repo.findoutgoing(other, force=opts['force'])
336 o = repo.findoutgoing(other, force=opts['force'])
337
337
338 if revs:
338 if revs:
339 cg = repo.changegroupsubset(o, revs, 'bundle')
339 cg = repo.changegroupsubset(o, revs, 'bundle')
340 else:
340 else:
341 cg = repo.changegroup(o, 'bundle')
341 cg = repo.changegroup(o, 'bundle')
342 changegroup.writebundle(cg, fname, "HG10BZ")
342 changegroup.writebundle(cg, fname, "HG10BZ")
343
343
344 def cat(ui, repo, file1, *pats, **opts):
344 def cat(ui, repo, file1, *pats, **opts):
345 """output the current or given revision of files
345 """output the current or given revision of files
346
346
347 Print the specified files as they were at the given revision.
347 Print the specified files as they were at the given revision.
348 If no revision is given, the parent of the working directory is used,
348 If no revision is given, the parent of the working directory is used,
349 or tip if no revision is checked out.
349 or tip if no revision is checked out.
350
350
351 Output may be to a file, in which case the name of the file is
351 Output may be to a file, in which case the name of the file is
352 given using a format string. The formatting rules are the same as
352 given using a format string. The formatting rules are the same as
353 for the export command, with the following additions:
353 for the export command, with the following additions:
354
354
355 %s basename of file being printed
355 %s basename of file being printed
356 %d dirname of file being printed, or '.' if in repo root
356 %d dirname of file being printed, or '.' if in repo root
357 %p root-relative path name of file being printed
357 %p root-relative path name of file being printed
358 """
358 """
359 ctx = repo.changectx(opts['rev'])
359 ctx = repo.changectx(opts['rev'])
360 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
360 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
361 ctx.node()):
361 ctx.node()):
362 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
362 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
363 fp.write(ctx.filectx(abs).data())
363 fp.write(ctx.filectx(abs).data())
364
364
365 def clone(ui, source, dest=None, **opts):
365 def clone(ui, source, dest=None, **opts):
366 """make a copy of an existing repository
366 """make a copy of an existing repository
367
367
368 Create a copy of an existing repository in a new directory.
368 Create a copy of an existing repository in a new directory.
369
369
370 If no destination directory name is specified, it defaults to the
370 If no destination directory name is specified, it defaults to the
371 basename of the source.
371 basename of the source.
372
372
373 The location of the source is added to the new repository's
373 The location of the source is added to the new repository's
374 .hg/hgrc file, as the default to be used for future pulls.
374 .hg/hgrc file, as the default to be used for future pulls.
375
375
376 For efficiency, hardlinks are used for cloning whenever the source
376 For efficiency, hardlinks are used for cloning whenever the source
377 and destination are on the same filesystem (note this applies only
377 and destination are on the same filesystem (note this applies only
378 to the repository data, not to the checked out files). Some
378 to the repository data, not to the checked out files). Some
379 filesystems, such as AFS, implement hardlinking incorrectly, but
379 filesystems, such as AFS, implement hardlinking incorrectly, but
380 do not report errors. In these cases, use the --pull option to
380 do not report errors. In these cases, use the --pull option to
381 avoid hardlinking.
381 avoid hardlinking.
382
382
383 You can safely clone repositories and checked out files using full
383 You can safely clone repositories and checked out files using full
384 hardlinks with
384 hardlinks with
385
385
386 $ cp -al REPO REPOCLONE
386 $ cp -al REPO REPOCLONE
387
387
388 which is the fastest way to clone. However, the operation is not
388 which is the fastest way to clone. However, the operation is not
389 atomic (making sure REPO is not modified during the operation is
389 atomic (making sure REPO is not modified during the operation is
390 up to you) and you have to make sure your editor breaks hardlinks
390 up to you) and you have to make sure your editor breaks hardlinks
391 (Emacs and most Linux Kernel tools do so).
391 (Emacs and most Linux Kernel tools do so).
392
392
393 If you use the -r option to clone up to a specific revision, no
393 If you use the -r option to clone up to a specific revision, no
394 subsequent revisions will be present in the cloned repository.
394 subsequent revisions will be present in the cloned repository.
395 This option implies --pull, even on local repositories.
395 This option implies --pull, even on local repositories.
396
396
397 See pull for valid source format details.
397 See pull for valid source format details.
398
398
399 It is possible to specify an ssh:// URL as the destination, but no
399 It is possible to specify an ssh:// URL as the destination, but no
400 .hg/hgrc and working directory will be created on the remote side.
400 .hg/hgrc and working directory will be created on the remote side.
401 Look at the help text for the pull command for important details
401 Look at the help text for the pull command for important details
402 about ssh:// URLs.
402 about ssh:// URLs.
403 """
403 """
404 setremoteconfig(ui, opts)
404 setremoteconfig(ui, opts)
405 hg.clone(ui, ui.expandpath(source), dest,
405 hg.clone(ui, ui.expandpath(source), dest,
406 pull=opts['pull'],
406 pull=opts['pull'],
407 stream=opts['uncompressed'],
407 stream=opts['uncompressed'],
408 rev=opts['rev'],
408 rev=opts['rev'],
409 update=not opts['noupdate'])
409 update=not opts['noupdate'])
410
410
411 def commit(ui, repo, *pats, **opts):
411 def commit(ui, repo, *pats, **opts):
412 """commit the specified files or all outstanding changes
412 """commit the specified files or all outstanding changes
413
413
414 Commit changes to the given files into the repository.
414 Commit changes to the given files into the repository.
415
415
416 If a list of files is omitted, all changes reported by "hg status"
416 If a list of files is omitted, all changes reported by "hg status"
417 will be committed.
417 will be committed.
418
418
419 If no commit message is specified, the editor configured in your hgrc
419 If no commit message is specified, the editor configured in your hgrc
420 or in the EDITOR environment variable is started to enter a message.
420 or in the EDITOR environment variable is started to enter a message.
421 """
421 """
422 message = logmessage(opts)
422 message = logmessage(opts)
423
423
424 if opts['addremove']:
424 if opts['addremove']:
425 cmdutil.addremove(repo, pats, opts)
425 cmdutil.addremove(repo, pats, opts)
426 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
426 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
427 if pats:
427 if pats:
428 status = repo.status(files=fns, match=match)
428 status = repo.status(files=fns, match=match)
429 modified, added, removed, deleted, unknown = status[:5]
429 modified, added, removed, deleted, unknown = status[:5]
430 files = modified + added + removed
430 files = modified + added + removed
431 slist = None
431 slist = None
432 for f in fns:
432 for f in fns:
433 if f == '.':
434 continue
433 if f not in files:
435 if f not in files:
434 rf = repo.wjoin(f)
436 rf = repo.wjoin(f)
435 if f in unknown:
437 if f in unknown:
436 raise util.Abort(_("file %s not tracked!") % rf)
438 raise util.Abort(_("file %s not tracked!") % rf)
437 try:
439 try:
438 mode = os.lstat(rf)[stat.ST_MODE]
440 mode = os.lstat(rf)[stat.ST_MODE]
439 except OSError:
441 except OSError:
440 raise util.Abort(_("file %s not found!") % rf)
442 raise util.Abort(_("file %s not found!") % rf)
441 if stat.S_ISDIR(mode):
443 if stat.S_ISDIR(mode):
442 name = f + '/'
444 name = f + '/'
443 if slist is None:
445 if slist is None:
444 slist = list(files)
446 slist = list(files)
445 slist.sort()
447 slist.sort()
446 i = bisect.bisect(slist, name)
448 i = bisect.bisect(slist, name)
447 if i >= len(slist) or not slist[i].startswith(name):
449 if i >= len(slist) or not slist[i].startswith(name):
448 raise util.Abort(_("no match under directory %s!")
450 raise util.Abort(_("no match under directory %s!")
449 % rf)
451 % rf)
450 elif not stat.S_ISREG(mode):
452 elif not stat.S_ISREG(mode):
451 raise util.Abort(_("can't commit %s: "
453 raise util.Abort(_("can't commit %s: "
452 "unsupported file type!") % rf)
454 "unsupported file type!") % rf)
453 else:
455 else:
454 files = []
456 files = []
455 try:
457 try:
456 repo.commit(files, message, opts['user'], opts['date'], match,
458 repo.commit(files, message, opts['user'], opts['date'], match,
457 force_editor=opts.get('force_editor'))
459 force_editor=opts.get('force_editor'))
458 except ValueError, inst:
460 except ValueError, inst:
459 raise util.Abort(str(inst))
461 raise util.Abort(str(inst))
460
462
461 def docopy(ui, repo, pats, opts, wlock):
463 def docopy(ui, repo, pats, opts, wlock):
462 # called with the repo lock held
464 # called with the repo lock held
463 #
465 #
464 # hgsep => pathname that uses "/" to separate directories
466 # hgsep => pathname that uses "/" to separate directories
465 # ossep => pathname that uses os.sep to separate directories
467 # ossep => pathname that uses os.sep to separate directories
466 cwd = repo.getcwd()
468 cwd = repo.getcwd()
467 errors = 0
469 errors = 0
468 copied = []
470 copied = []
469 targets = {}
471 targets = {}
470
472
471 # abs: hgsep
473 # abs: hgsep
472 # rel: ossep
474 # rel: ossep
473 # return: hgsep
475 # return: hgsep
474 def okaytocopy(abs, rel, exact):
476 def okaytocopy(abs, rel, exact):
475 reasons = {'?': _('is not managed'),
477 reasons = {'?': _('is not managed'),
476 'a': _('has been marked for add'),
478 'a': _('has been marked for add'),
477 'r': _('has been marked for remove')}
479 'r': _('has been marked for remove')}
478 state = repo.dirstate.state(abs)
480 state = repo.dirstate.state(abs)
479 reason = reasons.get(state)
481 reason = reasons.get(state)
480 if reason:
482 if reason:
481 if state == 'a':
483 if state == 'a':
482 origsrc = repo.dirstate.copied(abs)
484 origsrc = repo.dirstate.copied(abs)
483 if origsrc is not None:
485 if origsrc is not None:
484 return origsrc
486 return origsrc
485 if exact:
487 if exact:
486 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
488 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
487 else:
489 else:
488 return abs
490 return abs
489
491
490 # origsrc: hgsep
492 # origsrc: hgsep
491 # abssrc: hgsep
493 # abssrc: hgsep
492 # relsrc: ossep
494 # relsrc: ossep
493 # target: ossep
495 # target: ossep
494 def copy(origsrc, abssrc, relsrc, target, exact):
496 def copy(origsrc, abssrc, relsrc, target, exact):
495 abstarget = util.canonpath(repo.root, cwd, target)
497 abstarget = util.canonpath(repo.root, cwd, target)
496 reltarget = util.pathto(repo.root, cwd, abstarget)
498 reltarget = util.pathto(repo.root, cwd, abstarget)
497 prevsrc = targets.get(abstarget)
499 prevsrc = targets.get(abstarget)
498 if prevsrc is not None:
500 if prevsrc is not None:
499 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
501 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
500 (reltarget, util.localpath(abssrc),
502 (reltarget, util.localpath(abssrc),
501 util.localpath(prevsrc)))
503 util.localpath(prevsrc)))
502 return
504 return
503 if (not opts['after'] and os.path.exists(reltarget) or
505 if (not opts['after'] and os.path.exists(reltarget) or
504 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
506 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
505 if not opts['force']:
507 if not opts['force']:
506 ui.warn(_('%s: not overwriting - file exists\n') %
508 ui.warn(_('%s: not overwriting - file exists\n') %
507 reltarget)
509 reltarget)
508 return
510 return
509 if not opts['after'] and not opts.get('dry_run'):
511 if not opts['after'] and not opts.get('dry_run'):
510 os.unlink(reltarget)
512 os.unlink(reltarget)
511 if opts['after']:
513 if opts['after']:
512 if not os.path.exists(reltarget):
514 if not os.path.exists(reltarget):
513 return
515 return
514 else:
516 else:
515 targetdir = os.path.dirname(reltarget) or '.'
517 targetdir = os.path.dirname(reltarget) or '.'
516 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
518 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
517 os.makedirs(targetdir)
519 os.makedirs(targetdir)
518 try:
520 try:
519 restore = repo.dirstate.state(abstarget) == 'r'
521 restore = repo.dirstate.state(abstarget) == 'r'
520 if restore and not opts.get('dry_run'):
522 if restore and not opts.get('dry_run'):
521 repo.undelete([abstarget], wlock)
523 repo.undelete([abstarget], wlock)
522 try:
524 try:
523 if not opts.get('dry_run'):
525 if not opts.get('dry_run'):
524 util.copyfile(relsrc, reltarget)
526 util.copyfile(relsrc, reltarget)
525 restore = False
527 restore = False
526 finally:
528 finally:
527 if restore:
529 if restore:
528 repo.remove([abstarget], wlock)
530 repo.remove([abstarget], wlock)
529 except IOError, inst:
531 except IOError, inst:
530 if inst.errno == errno.ENOENT:
532 if inst.errno == errno.ENOENT:
531 ui.warn(_('%s: deleted in working copy\n') % relsrc)
533 ui.warn(_('%s: deleted in working copy\n') % relsrc)
532 else:
534 else:
533 ui.warn(_('%s: cannot copy - %s\n') %
535 ui.warn(_('%s: cannot copy - %s\n') %
534 (relsrc, inst.strerror))
536 (relsrc, inst.strerror))
535 errors += 1
537 errors += 1
536 return
538 return
537 if ui.verbose or not exact:
539 if ui.verbose or not exact:
538 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
540 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
539 targets[abstarget] = abssrc
541 targets[abstarget] = abssrc
540 if abstarget != origsrc and not opts.get('dry_run'):
542 if abstarget != origsrc and not opts.get('dry_run'):
541 repo.copy(origsrc, abstarget, wlock)
543 repo.copy(origsrc, abstarget, wlock)
542 copied.append((abssrc, relsrc, exact))
544 copied.append((abssrc, relsrc, exact))
543
545
544 # pat: ossep
546 # pat: ossep
545 # dest ossep
547 # dest ossep
546 # srcs: list of (hgsep, hgsep, ossep, bool)
548 # srcs: list of (hgsep, hgsep, ossep, bool)
547 # return: function that takes hgsep and returns ossep
549 # return: function that takes hgsep and returns ossep
548 def targetpathfn(pat, dest, srcs):
550 def targetpathfn(pat, dest, srcs):
549 if os.path.isdir(pat):
551 if os.path.isdir(pat):
550 abspfx = util.canonpath(repo.root, cwd, pat)
552 abspfx = util.canonpath(repo.root, cwd, pat)
551 abspfx = util.localpath(abspfx)
553 abspfx = util.localpath(abspfx)
552 if destdirexists:
554 if destdirexists:
553 striplen = len(os.path.split(abspfx)[0])
555 striplen = len(os.path.split(abspfx)[0])
554 else:
556 else:
555 striplen = len(abspfx)
557 striplen = len(abspfx)
556 if striplen:
558 if striplen:
557 striplen += len(os.sep)
559 striplen += len(os.sep)
558 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
560 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
559 elif destdirexists:
561 elif destdirexists:
560 res = lambda p: os.path.join(dest,
562 res = lambda p: os.path.join(dest,
561 os.path.basename(util.localpath(p)))
563 os.path.basename(util.localpath(p)))
562 else:
564 else:
563 res = lambda p: dest
565 res = lambda p: dest
564 return res
566 return res
565
567
566 # pat: ossep
568 # pat: ossep
567 # dest ossep
569 # dest ossep
568 # srcs: list of (hgsep, hgsep, ossep, bool)
570 # srcs: list of (hgsep, hgsep, ossep, bool)
569 # return: function that takes hgsep and returns ossep
571 # return: function that takes hgsep and returns ossep
570 def targetpathafterfn(pat, dest, srcs):
572 def targetpathafterfn(pat, dest, srcs):
571 if util.patkind(pat, None)[0]:
573 if util.patkind(pat, None)[0]:
572 # a mercurial pattern
574 # a mercurial pattern
573 res = lambda p: os.path.join(dest,
575 res = lambda p: os.path.join(dest,
574 os.path.basename(util.localpath(p)))
576 os.path.basename(util.localpath(p)))
575 else:
577 else:
576 abspfx = util.canonpath(repo.root, cwd, pat)
578 abspfx = util.canonpath(repo.root, cwd, pat)
577 if len(abspfx) < len(srcs[0][0]):
579 if len(abspfx) < len(srcs[0][0]):
578 # A directory. Either the target path contains the last
580 # A directory. Either the target path contains the last
579 # component of the source path or it does not.
581 # component of the source path or it does not.
580 def evalpath(striplen):
582 def evalpath(striplen):
581 score = 0
583 score = 0
582 for s in srcs:
584 for s in srcs:
583 t = os.path.join(dest, util.localpath(s[0])[striplen:])
585 t = os.path.join(dest, util.localpath(s[0])[striplen:])
584 if os.path.exists(t):
586 if os.path.exists(t):
585 score += 1
587 score += 1
586 return score
588 return score
587
589
588 abspfx = util.localpath(abspfx)
590 abspfx = util.localpath(abspfx)
589 striplen = len(abspfx)
591 striplen = len(abspfx)
590 if striplen:
592 if striplen:
591 striplen += len(os.sep)
593 striplen += len(os.sep)
592 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
594 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
593 score = evalpath(striplen)
595 score = evalpath(striplen)
594 striplen1 = len(os.path.split(abspfx)[0])
596 striplen1 = len(os.path.split(abspfx)[0])
595 if striplen1:
597 if striplen1:
596 striplen1 += len(os.sep)
598 striplen1 += len(os.sep)
597 if evalpath(striplen1) > score:
599 if evalpath(striplen1) > score:
598 striplen = striplen1
600 striplen = striplen1
599 res = lambda p: os.path.join(dest,
601 res = lambda p: os.path.join(dest,
600 util.localpath(p)[striplen:])
602 util.localpath(p)[striplen:])
601 else:
603 else:
602 # a file
604 # a file
603 if destdirexists:
605 if destdirexists:
604 res = lambda p: os.path.join(dest,
606 res = lambda p: os.path.join(dest,
605 os.path.basename(util.localpath(p)))
607 os.path.basename(util.localpath(p)))
606 else:
608 else:
607 res = lambda p: dest
609 res = lambda p: dest
608 return res
610 return res
609
611
610
612
611 pats = util.expand_glob(pats)
613 pats = util.expand_glob(pats)
612 if not pats:
614 if not pats:
613 raise util.Abort(_('no source or destination specified'))
615 raise util.Abort(_('no source or destination specified'))
614 if len(pats) == 1:
616 if len(pats) == 1:
615 raise util.Abort(_('no destination specified'))
617 raise util.Abort(_('no destination specified'))
616 dest = pats.pop()
618 dest = pats.pop()
617 destdirexists = os.path.isdir(dest)
619 destdirexists = os.path.isdir(dest)
618 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
620 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
619 raise util.Abort(_('with multiple sources, destination must be an '
621 raise util.Abort(_('with multiple sources, destination must be an '
620 'existing directory'))
622 'existing directory'))
621 if opts['after']:
623 if opts['after']:
622 tfn = targetpathafterfn
624 tfn = targetpathafterfn
623 else:
625 else:
624 tfn = targetpathfn
626 tfn = targetpathfn
625 copylist = []
627 copylist = []
626 for pat in pats:
628 for pat in pats:
627 srcs = []
629 srcs = []
628 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
630 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
629 globbed=True):
631 globbed=True):
630 origsrc = okaytocopy(abssrc, relsrc, exact)
632 origsrc = okaytocopy(abssrc, relsrc, exact)
631 if origsrc:
633 if origsrc:
632 srcs.append((origsrc, abssrc, relsrc, exact))
634 srcs.append((origsrc, abssrc, relsrc, exact))
633 if not srcs:
635 if not srcs:
634 continue
636 continue
635 copylist.append((tfn(pat, dest, srcs), srcs))
637 copylist.append((tfn(pat, dest, srcs), srcs))
636 if not copylist:
638 if not copylist:
637 raise util.Abort(_('no files to copy'))
639 raise util.Abort(_('no files to copy'))
638
640
639 for targetpath, srcs in copylist:
641 for targetpath, srcs in copylist:
640 for origsrc, abssrc, relsrc, exact in srcs:
642 for origsrc, abssrc, relsrc, exact in srcs:
641 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
643 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
642
644
643 if errors:
645 if errors:
644 ui.warn(_('(consider using --after)\n'))
646 ui.warn(_('(consider using --after)\n'))
645 return errors, copied
647 return errors, copied
646
648
647 def copy(ui, repo, *pats, **opts):
649 def copy(ui, repo, *pats, **opts):
648 """mark files as copied for the next commit
650 """mark files as copied for the next commit
649
651
650 Mark dest as having copies of source files. If dest is a
652 Mark dest as having copies of source files. If dest is a
651 directory, copies are put in that directory. If dest is a file,
653 directory, copies are put in that directory. If dest is a file,
652 there can only be one source.
654 there can only be one source.
653
655
654 By default, this command copies the contents of files as they
656 By default, this command copies the contents of files as they
655 stand in the working directory. If invoked with --after, the
657 stand in the working directory. If invoked with --after, the
656 operation is recorded, but no copying is performed.
658 operation is recorded, but no copying is performed.
657
659
658 This command takes effect in the next commit. To undo a copy
660 This command takes effect in the next commit. To undo a copy
659 before that, see hg revert.
661 before that, see hg revert.
660 """
662 """
661 wlock = repo.wlock(0)
663 wlock = repo.wlock(0)
662 errs, copied = docopy(ui, repo, pats, opts, wlock)
664 errs, copied = docopy(ui, repo, pats, opts, wlock)
663 return errs
665 return errs
664
666
665 def debugancestor(ui, index, rev1, rev2):
667 def debugancestor(ui, index, rev1, rev2):
666 """find the ancestor revision of two revisions in a given index"""
668 """find the ancestor revision of two revisions in a given index"""
667 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
669 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
668 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
670 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
669 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
671 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
670
672
671 def debugcomplete(ui, cmd='', **opts):
673 def debugcomplete(ui, cmd='', **opts):
672 """returns the completion list associated with the given command"""
674 """returns the completion list associated with the given command"""
673
675
674 if opts['options']:
676 if opts['options']:
675 options = []
677 options = []
676 otables = [globalopts]
678 otables = [globalopts]
677 if cmd:
679 if cmd:
678 aliases, entry = findcmd(ui, cmd)
680 aliases, entry = findcmd(ui, cmd)
679 otables.append(entry[1])
681 otables.append(entry[1])
680 for t in otables:
682 for t in otables:
681 for o in t:
683 for o in t:
682 if o[0]:
684 if o[0]:
683 options.append('-%s' % o[0])
685 options.append('-%s' % o[0])
684 options.append('--%s' % o[1])
686 options.append('--%s' % o[1])
685 ui.write("%s\n" % "\n".join(options))
687 ui.write("%s\n" % "\n".join(options))
686 return
688 return
687
689
688 clist = findpossible(ui, cmd).keys()
690 clist = findpossible(ui, cmd).keys()
689 clist.sort()
691 clist.sort()
690 ui.write("%s\n" % "\n".join(clist))
692 ui.write("%s\n" % "\n".join(clist))
691
693
692 def debugrebuildstate(ui, repo, rev=""):
694 def debugrebuildstate(ui, repo, rev=""):
693 """rebuild the dirstate as it would look like for the given revision"""
695 """rebuild the dirstate as it would look like for the given revision"""
694 if rev == "":
696 if rev == "":
695 rev = repo.changelog.tip()
697 rev = repo.changelog.tip()
696 ctx = repo.changectx(rev)
698 ctx = repo.changectx(rev)
697 files = ctx.manifest()
699 files = ctx.manifest()
698 wlock = repo.wlock()
700 wlock = repo.wlock()
699 repo.dirstate.rebuild(rev, files)
701 repo.dirstate.rebuild(rev, files)
700
702
701 def debugcheckstate(ui, repo):
703 def debugcheckstate(ui, repo):
702 """validate the correctness of the current dirstate"""
704 """validate the correctness of the current dirstate"""
703 parent1, parent2 = repo.dirstate.parents()
705 parent1, parent2 = repo.dirstate.parents()
704 repo.dirstate.read()
706 repo.dirstate.read()
705 dc = repo.dirstate.map
707 dc = repo.dirstate.map
706 keys = dc.keys()
708 keys = dc.keys()
707 keys.sort()
709 keys.sort()
708 m1 = repo.changectx(parent1).manifest()
710 m1 = repo.changectx(parent1).manifest()
709 m2 = repo.changectx(parent2).manifest()
711 m2 = repo.changectx(parent2).manifest()
710 errors = 0
712 errors = 0
711 for f in dc:
713 for f in dc:
712 state = repo.dirstate.state(f)
714 state = repo.dirstate.state(f)
713 if state in "nr" and f not in m1:
715 if state in "nr" and f not in m1:
714 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
716 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
715 errors += 1
717 errors += 1
716 if state in "a" and f in m1:
718 if state in "a" and f in m1:
717 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
719 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
718 errors += 1
720 errors += 1
719 if state in "m" and f not in m1 and f not in m2:
721 if state in "m" and f not in m1 and f not in m2:
720 ui.warn(_("%s in state %s, but not in either manifest\n") %
722 ui.warn(_("%s in state %s, but not in either manifest\n") %
721 (f, state))
723 (f, state))
722 errors += 1
724 errors += 1
723 for f in m1:
725 for f in m1:
724 state = repo.dirstate.state(f)
726 state = repo.dirstate.state(f)
725 if state not in "nrm":
727 if state not in "nrm":
726 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
728 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
727 errors += 1
729 errors += 1
728 if errors:
730 if errors:
729 error = _(".hg/dirstate inconsistent with current parent's manifest")
731 error = _(".hg/dirstate inconsistent with current parent's manifest")
730 raise util.Abort(error)
732 raise util.Abort(error)
731
733
732 def showconfig(ui, repo, *values, **opts):
734 def showconfig(ui, repo, *values, **opts):
733 """show combined config settings from all hgrc files
735 """show combined config settings from all hgrc files
734
736
735 With no args, print names and values of all config items.
737 With no args, print names and values of all config items.
736
738
737 With one arg of the form section.name, print just the value of
739 With one arg of the form section.name, print just the value of
738 that config item.
740 that config item.
739
741
740 With multiple args, print names and values of all config items
742 With multiple args, print names and values of all config items
741 with matching section names."""
743 with matching section names."""
742
744
743 untrusted = bool(opts.get('untrusted'))
745 untrusted = bool(opts.get('untrusted'))
744 if values:
746 if values:
745 if len([v for v in values if '.' in v]) > 1:
747 if len([v for v in values if '.' in v]) > 1:
746 raise util.Abort(_('only one config item permitted'))
748 raise util.Abort(_('only one config item permitted'))
747 for section, name, value in ui.walkconfig(untrusted=untrusted):
749 for section, name, value in ui.walkconfig(untrusted=untrusted):
748 sectname = section + '.' + name
750 sectname = section + '.' + name
749 if values:
751 if values:
750 for v in values:
752 for v in values:
751 if v == section:
753 if v == section:
752 ui.write('%s=%s\n' % (sectname, value))
754 ui.write('%s=%s\n' % (sectname, value))
753 elif v == sectname:
755 elif v == sectname:
754 ui.write(value, '\n')
756 ui.write(value, '\n')
755 else:
757 else:
756 ui.write('%s=%s\n' % (sectname, value))
758 ui.write('%s=%s\n' % (sectname, value))
757
759
758 def debugsetparents(ui, repo, rev1, rev2=None):
760 def debugsetparents(ui, repo, rev1, rev2=None):
759 """manually set the parents of the current working directory
761 """manually set the parents of the current working directory
760
762
761 This is useful for writing repository conversion tools, but should
763 This is useful for writing repository conversion tools, but should
762 be used with care.
764 be used with care.
763 """
765 """
764
766
765 if not rev2:
767 if not rev2:
766 rev2 = hex(nullid)
768 rev2 = hex(nullid)
767
769
768 wlock = repo.wlock()
770 wlock = repo.wlock()
769 try:
771 try:
770 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
772 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
771 finally:
773 finally:
772 wlock.release()
774 wlock.release()
773
775
774 def debugstate(ui, repo):
776 def debugstate(ui, repo):
775 """show the contents of the current dirstate"""
777 """show the contents of the current dirstate"""
776 repo.dirstate.read()
778 repo.dirstate.read()
777 dc = repo.dirstate.map
779 dc = repo.dirstate.map
778 keys = dc.keys()
780 keys = dc.keys()
779 keys.sort()
781 keys.sort()
780 for file_ in keys:
782 for file_ in keys:
781 if dc[file_][3] == -1:
783 if dc[file_][3] == -1:
782 # Pad or slice to locale representation
784 # Pad or slice to locale representation
783 locale_len = len(time.strftime("%x %X", time.localtime(0)))
785 locale_len = len(time.strftime("%x %X", time.localtime(0)))
784 timestr = 'unset'
786 timestr = 'unset'
785 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
787 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
786 else:
788 else:
787 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
789 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
788 ui.write("%c %3o %10d %s %s\n"
790 ui.write("%c %3o %10d %s %s\n"
789 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
791 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
790 timestr, file_))
792 timestr, file_))
791 for f in repo.dirstate.copies():
793 for f in repo.dirstate.copies():
792 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
794 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
793
795
794 def debugdata(ui, file_, rev):
796 def debugdata(ui, file_, rev):
795 """dump the contents of an data file revision"""
797 """dump the contents of an data file revision"""
796 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
798 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
797 file_[:-2] + ".i", file_, 0)
799 file_[:-2] + ".i", file_, 0)
798 try:
800 try:
799 ui.write(r.revision(r.lookup(rev)))
801 ui.write(r.revision(r.lookup(rev)))
800 except KeyError:
802 except KeyError:
801 raise util.Abort(_('invalid revision identifier %s') % rev)
803 raise util.Abort(_('invalid revision identifier %s') % rev)
802
804
803 def debugdate(ui, date, range=None, **opts):
805 def debugdate(ui, date, range=None, **opts):
804 """parse and display a date"""
806 """parse and display a date"""
805 if opts["extended"]:
807 if opts["extended"]:
806 d = util.parsedate(date, util.extendeddateformats)
808 d = util.parsedate(date, util.extendeddateformats)
807 else:
809 else:
808 d = util.parsedate(date)
810 d = util.parsedate(date)
809 ui.write("internal: %s %s\n" % d)
811 ui.write("internal: %s %s\n" % d)
810 ui.write("standard: %s\n" % util.datestr(d))
812 ui.write("standard: %s\n" % util.datestr(d))
811 if range:
813 if range:
812 m = util.matchdate(range)
814 m = util.matchdate(range)
813 ui.write("match: %s\n" % m(d[0]))
815 ui.write("match: %s\n" % m(d[0]))
814
816
815 def debugindex(ui, file_):
817 def debugindex(ui, file_):
816 """dump the contents of an index file"""
818 """dump the contents of an index file"""
817 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
819 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
818 ui.write(" rev offset length base linkrev" +
820 ui.write(" rev offset length base linkrev" +
819 " nodeid p1 p2\n")
821 " nodeid p1 p2\n")
820 for i in xrange(r.count()):
822 for i in xrange(r.count()):
821 node = r.node(i)
823 node = r.node(i)
822 pp = r.parents(node)
824 pp = r.parents(node)
823 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
825 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
824 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
826 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
825 short(node), short(pp[0]), short(pp[1])))
827 short(node), short(pp[0]), short(pp[1])))
826
828
827 def debugindexdot(ui, file_):
829 def debugindexdot(ui, file_):
828 """dump an index DAG as a .dot file"""
830 """dump an index DAG as a .dot file"""
829 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
831 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
830 ui.write("digraph G {\n")
832 ui.write("digraph G {\n")
831 for i in xrange(r.count()):
833 for i in xrange(r.count()):
832 node = r.node(i)
834 node = r.node(i)
833 pp = r.parents(node)
835 pp = r.parents(node)
834 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
836 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
835 if pp[1] != nullid:
837 if pp[1] != nullid:
836 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
838 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
837 ui.write("}\n")
839 ui.write("}\n")
838
840
839 def debuginstall(ui):
841 def debuginstall(ui):
840 '''test Mercurial installation'''
842 '''test Mercurial installation'''
841
843
842 def writetemp(contents):
844 def writetemp(contents):
843 (fd, name) = tempfile.mkstemp()
845 (fd, name) = tempfile.mkstemp()
844 f = os.fdopen(fd, "wb")
846 f = os.fdopen(fd, "wb")
845 f.write(contents)
847 f.write(contents)
846 f.close()
848 f.close()
847 return name
849 return name
848
850
849 problems = 0
851 problems = 0
850
852
851 # encoding
853 # encoding
852 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
854 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
853 try:
855 try:
854 util.fromlocal("test")
856 util.fromlocal("test")
855 except util.Abort, inst:
857 except util.Abort, inst:
856 ui.write(" %s\n" % inst)
858 ui.write(" %s\n" % inst)
857 ui.write(_(" (check that your locale is properly set)\n"))
859 ui.write(_(" (check that your locale is properly set)\n"))
858 problems += 1
860 problems += 1
859
861
860 # compiled modules
862 # compiled modules
861 ui.status(_("Checking extensions...\n"))
863 ui.status(_("Checking extensions...\n"))
862 try:
864 try:
863 import bdiff, mpatch, base85
865 import bdiff, mpatch, base85
864 except Exception, inst:
866 except Exception, inst:
865 ui.write(" %s\n" % inst)
867 ui.write(" %s\n" % inst)
866 ui.write(_(" One or more extensions could not be found"))
868 ui.write(_(" One or more extensions could not be found"))
867 ui.write(_(" (check that you compiled the extensions)\n"))
869 ui.write(_(" (check that you compiled the extensions)\n"))
868 problems += 1
870 problems += 1
869
871
870 # templates
872 # templates
871 ui.status(_("Checking templates...\n"))
873 ui.status(_("Checking templates...\n"))
872 try:
874 try:
873 import templater
875 import templater
874 t = templater.templater(templater.templatepath("map-cmdline.default"))
876 t = templater.templater(templater.templatepath("map-cmdline.default"))
875 except Exception, inst:
877 except Exception, inst:
876 ui.write(" %s\n" % inst)
878 ui.write(" %s\n" % inst)
877 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
879 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
878 problems += 1
880 problems += 1
879
881
880 # patch
882 # patch
881 ui.status(_("Checking patch...\n"))
883 ui.status(_("Checking patch...\n"))
882 path = os.environ.get('PATH', '')
884 path = os.environ.get('PATH', '')
883 patcher = util.find_in_path('gpatch', path,
885 patcher = util.find_in_path('gpatch', path,
884 util.find_in_path('patch', path, None))
886 util.find_in_path('patch', path, None))
885 if not patcher:
887 if not patcher:
886 ui.write(_(" Can't find patch or gpatch in PATH\n"))
888 ui.write(_(" Can't find patch or gpatch in PATH\n"))
887 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
889 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
888 problems += 1
890 problems += 1
889 else:
891 else:
890 # actually attempt a patch here
892 # actually attempt a patch here
891 a = "1\n2\n3\n4\n"
893 a = "1\n2\n3\n4\n"
892 b = "1\n2\n3\ninsert\n4\n"
894 b = "1\n2\n3\ninsert\n4\n"
893 d = mdiff.unidiff(a, None, b, None, "a")
895 d = mdiff.unidiff(a, None, b, None, "a")
894 fa = writetemp(a)
896 fa = writetemp(a)
895 fd = writetemp(d)
897 fd = writetemp(d)
896 fp = os.popen('%s %s %s' % (patcher, fa, fd))
898 fp = os.popen('%s %s %s' % (patcher, fa, fd))
897 files = []
899 files = []
898 output = ""
900 output = ""
899 for line in fp:
901 for line in fp:
900 output += line
902 output += line
901 if line.startswith('patching file '):
903 if line.startswith('patching file '):
902 pf = util.parse_patch_output(line.rstrip())
904 pf = util.parse_patch_output(line.rstrip())
903 files.append(pf)
905 files.append(pf)
904 if files != [fa]:
906 if files != [fa]:
905 ui.write(_(" unexpected patch output!"))
907 ui.write(_(" unexpected patch output!"))
906 ui.write(_(" (you may have an incompatible version of patch)\n"))
908 ui.write(_(" (you may have an incompatible version of patch)\n"))
907 ui.write(output)
909 ui.write(output)
908 problems += 1
910 problems += 1
909 a = file(fa).read()
911 a = file(fa).read()
910 if a != b:
912 if a != b:
911 ui.write(_(" patch test failed!"))
913 ui.write(_(" patch test failed!"))
912 ui.write(_(" (you may have an incompatible version of patch)\n"))
914 ui.write(_(" (you may have an incompatible version of patch)\n"))
913 problems += 1
915 problems += 1
914 os.unlink(fa)
916 os.unlink(fa)
915 os.unlink(fd)
917 os.unlink(fd)
916
918
917 # merge helper
919 # merge helper
918 ui.status(_("Checking merge helper...\n"))
920 ui.status(_("Checking merge helper...\n"))
919 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
921 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
920 or "hgmerge")
922 or "hgmerge")
921 cmdpath = util.find_in_path(cmd, path)
923 cmdpath = util.find_in_path(cmd, path)
922 if not cmdpath:
924 if not cmdpath:
923 cmdpath = util.find_in_path(cmd.split()[0], path)
925 cmdpath = util.find_in_path(cmd.split()[0], path)
924 if not cmdpath:
926 if not cmdpath:
925 if cmd == 'hgmerge':
927 if cmd == 'hgmerge':
926 ui.write(_(" No merge helper set and can't find default"
928 ui.write(_(" No merge helper set and can't find default"
927 " hgmerge script in PATH\n"))
929 " hgmerge script in PATH\n"))
928 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
930 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
929 else:
931 else:
930 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
932 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
931 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
933 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
932 problems += 1
934 problems += 1
933 else:
935 else:
934 # actually attempt a patch here
936 # actually attempt a patch here
935 fa = writetemp("1\n2\n3\n4\n")
937 fa = writetemp("1\n2\n3\n4\n")
936 fl = writetemp("1\n2\n3\ninsert\n4\n")
938 fl = writetemp("1\n2\n3\ninsert\n4\n")
937 fr = writetemp("begin\n1\n2\n3\n4\n")
939 fr = writetemp("begin\n1\n2\n3\n4\n")
938 r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
940 r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
939 if r:
941 if r:
940 ui.write(_(" got unexpected merge error %d!") % r)
942 ui.write(_(" got unexpected merge error %d!") % r)
941 problems += 1
943 problems += 1
942 m = file(fl).read()
944 m = file(fl).read()
943 if m != "begin\n1\n2\n3\ninsert\n4\n":
945 if m != "begin\n1\n2\n3\ninsert\n4\n":
944 ui.write(_(" got unexpected merge results!") % r)
946 ui.write(_(" got unexpected merge results!") % r)
945 ui.write(_(" (your merge helper may have the"
947 ui.write(_(" (your merge helper may have the"
946 " wrong argument order)\n"))
948 " wrong argument order)\n"))
947 ui.write(m)
949 ui.write(m)
948 os.unlink(fa)
950 os.unlink(fa)
949 os.unlink(fl)
951 os.unlink(fl)
950 os.unlink(fr)
952 os.unlink(fr)
951
953
952 # editor
954 # editor
953 ui.status(_("Checking commit editor...\n"))
955 ui.status(_("Checking commit editor...\n"))
954 editor = (os.environ.get("HGEDITOR") or
956 editor = (os.environ.get("HGEDITOR") or
955 ui.config("ui", "editor") or
957 ui.config("ui", "editor") or
956 os.environ.get("EDITOR", "vi"))
958 os.environ.get("EDITOR", "vi"))
957 cmdpath = util.find_in_path(editor, path)
959 cmdpath = util.find_in_path(editor, path)
958 if not cmdpath:
960 if not cmdpath:
959 cmdpath = util.find_in_path(editor.split()[0], path)
961 cmdpath = util.find_in_path(editor.split()[0], path)
960 if not cmdpath:
962 if not cmdpath:
961 if editor == 'vi':
963 if editor == 'vi':
962 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
964 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
963 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
965 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
964 else:
966 else:
965 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
967 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
966 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
968 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
967 problems += 1
969 problems += 1
968
970
969 # check username
971 # check username
970 ui.status(_("Checking username...\n"))
972 ui.status(_("Checking username...\n"))
971 user = os.environ.get("HGUSER")
973 user = os.environ.get("HGUSER")
972 if user is None:
974 if user is None:
973 user = ui.config("ui", "username")
975 user = ui.config("ui", "username")
974 if user is None:
976 if user is None:
975 user = os.environ.get("EMAIL")
977 user = os.environ.get("EMAIL")
976 if not user:
978 if not user:
977 ui.warn(" ")
979 ui.warn(" ")
978 ui.username()
980 ui.username()
979 ui.write(_(" (specify a username in your .hgrc file)\n"))
981 ui.write(_(" (specify a username in your .hgrc file)\n"))
980
982
981 if not problems:
983 if not problems:
982 ui.status(_("No problems detected\n"))
984 ui.status(_("No problems detected\n"))
983 else:
985 else:
984 ui.write(_("%s problems detected,"
986 ui.write(_("%s problems detected,"
985 " please check your install!\n") % problems)
987 " please check your install!\n") % problems)
986
988
987 return problems
989 return problems
988
990
989 def debugrename(ui, repo, file1, *pats, **opts):
991 def debugrename(ui, repo, file1, *pats, **opts):
990 """dump rename information"""
992 """dump rename information"""
991
993
992 ctx = repo.changectx(opts.get('rev', 'tip'))
994 ctx = repo.changectx(opts.get('rev', 'tip'))
993 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
995 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
994 ctx.node()):
996 ctx.node()):
995 m = ctx.filectx(abs).renamed()
997 m = ctx.filectx(abs).renamed()
996 if m:
998 if m:
997 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
999 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
998 else:
1000 else:
999 ui.write(_("%s not renamed\n") % rel)
1001 ui.write(_("%s not renamed\n") % rel)
1000
1002
1001 def debugwalk(ui, repo, *pats, **opts):
1003 def debugwalk(ui, repo, *pats, **opts):
1002 """show how files match on given patterns"""
1004 """show how files match on given patterns"""
1003 items = list(cmdutil.walk(repo, pats, opts))
1005 items = list(cmdutil.walk(repo, pats, opts))
1004 if not items:
1006 if not items:
1005 return
1007 return
1006 fmt = '%%s %%-%ds %%-%ds %%s' % (
1008 fmt = '%%s %%-%ds %%-%ds %%s' % (
1007 max([len(abs) for (src, abs, rel, exact) in items]),
1009 max([len(abs) for (src, abs, rel, exact) in items]),
1008 max([len(rel) for (src, abs, rel, exact) in items]))
1010 max([len(rel) for (src, abs, rel, exact) in items]))
1009 for src, abs, rel, exact in items:
1011 for src, abs, rel, exact in items:
1010 line = fmt % (src, abs, rel, exact and 'exact' or '')
1012 line = fmt % (src, abs, rel, exact and 'exact' or '')
1011 ui.write("%s\n" % line.rstrip())
1013 ui.write("%s\n" % line.rstrip())
1012
1014
1013 def diff(ui, repo, *pats, **opts):
1015 def diff(ui, repo, *pats, **opts):
1014 """diff repository (or selected files)
1016 """diff repository (or selected files)
1015
1017
1016 Show differences between revisions for the specified files.
1018 Show differences between revisions for the specified files.
1017
1019
1018 Differences between files are shown using the unified diff format.
1020 Differences between files are shown using the unified diff format.
1019
1021
1020 NOTE: diff may generate unexpected results for merges, as it will
1022 NOTE: diff may generate unexpected results for merges, as it will
1021 default to comparing against the working directory's first parent
1023 default to comparing against the working directory's first parent
1022 changeset if no revisions are specified.
1024 changeset if no revisions are specified.
1023
1025
1024 When two revision arguments are given, then changes are shown
1026 When two revision arguments are given, then changes are shown
1025 between those revisions. If only one revision is specified then
1027 between those revisions. If only one revision is specified then
1026 that revision is compared to the working directory, and, when no
1028 that revision is compared to the working directory, and, when no
1027 revisions are specified, the working directory files are compared
1029 revisions are specified, the working directory files are compared
1028 to its parent.
1030 to its parent.
1029
1031
1030 Without the -a option, diff will avoid generating diffs of files
1032 Without the -a option, diff will avoid generating diffs of files
1031 it detects as binary. With -a, diff will generate a diff anyway,
1033 it detects as binary. With -a, diff will generate a diff anyway,
1032 probably with undesirable results.
1034 probably with undesirable results.
1033 """
1035 """
1034 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1036 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1035
1037
1036 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1038 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1037
1039
1038 patch.diff(repo, node1, node2, fns, match=matchfn,
1040 patch.diff(repo, node1, node2, fns, match=matchfn,
1039 opts=patch.diffopts(ui, opts))
1041 opts=patch.diffopts(ui, opts))
1040
1042
1041 def export(ui, repo, *changesets, **opts):
1043 def export(ui, repo, *changesets, **opts):
1042 """dump the header and diffs for one or more changesets
1044 """dump the header and diffs for one or more changesets
1043
1045
1044 Print the changeset header and diffs for one or more revisions.
1046 Print the changeset header and diffs for one or more revisions.
1045
1047
1046 The information shown in the changeset header is: author,
1048 The information shown in the changeset header is: author,
1047 changeset hash, parent(s) and commit comment.
1049 changeset hash, parent(s) and commit comment.
1048
1050
1049 NOTE: export may generate unexpected diff output for merge changesets,
1051 NOTE: export may generate unexpected diff output for merge changesets,
1050 as it will compare the merge changeset against its first parent only.
1052 as it will compare the merge changeset against its first parent only.
1051
1053
1052 Output may be to a file, in which case the name of the file is
1054 Output may be to a file, in which case the name of the file is
1053 given using a format string. The formatting rules are as follows:
1055 given using a format string. The formatting rules are as follows:
1054
1056
1055 %% literal "%" character
1057 %% literal "%" character
1056 %H changeset hash (40 bytes of hexadecimal)
1058 %H changeset hash (40 bytes of hexadecimal)
1057 %N number of patches being generated
1059 %N number of patches being generated
1058 %R changeset revision number
1060 %R changeset revision number
1059 %b basename of the exporting repository
1061 %b basename of the exporting repository
1060 %h short-form changeset hash (12 bytes of hexadecimal)
1062 %h short-form changeset hash (12 bytes of hexadecimal)
1061 %n zero-padded sequence number, starting at 1
1063 %n zero-padded sequence number, starting at 1
1062 %r zero-padded changeset revision number
1064 %r zero-padded changeset revision number
1063
1065
1064 Without the -a option, export will avoid generating diffs of files
1066 Without the -a option, export will avoid generating diffs of files
1065 it detects as binary. With -a, export will generate a diff anyway,
1067 it detects as binary. With -a, export will generate a diff anyway,
1066 probably with undesirable results.
1068 probably with undesirable results.
1067
1069
1068 With the --switch-parent option, the diff will be against the second
1070 With the --switch-parent option, the diff will be against the second
1069 parent. It can be useful to review a merge.
1071 parent. It can be useful to review a merge.
1070 """
1072 """
1071 if not changesets:
1073 if not changesets:
1072 raise util.Abort(_("export requires at least one changeset"))
1074 raise util.Abort(_("export requires at least one changeset"))
1073 revs = cmdutil.revrange(repo, changesets)
1075 revs = cmdutil.revrange(repo, changesets)
1074 if len(revs) > 1:
1076 if len(revs) > 1:
1075 ui.note(_('exporting patches:\n'))
1077 ui.note(_('exporting patches:\n'))
1076 else:
1078 else:
1077 ui.note(_('exporting patch:\n'))
1079 ui.note(_('exporting patch:\n'))
1078 patch.export(repo, revs, template=opts['output'],
1080 patch.export(repo, revs, template=opts['output'],
1079 switch_parent=opts['switch_parent'],
1081 switch_parent=opts['switch_parent'],
1080 opts=patch.diffopts(ui, opts))
1082 opts=patch.diffopts(ui, opts))
1081
1083
1082 def grep(ui, repo, pattern, *pats, **opts):
1084 def grep(ui, repo, pattern, *pats, **opts):
1083 """search for a pattern in specified files and revisions
1085 """search for a pattern in specified files and revisions
1084
1086
1085 Search revisions of files for a regular expression.
1087 Search revisions of files for a regular expression.
1086
1088
1087 This command behaves differently than Unix grep. It only accepts
1089 This command behaves differently than Unix grep. It only accepts
1088 Python/Perl regexps. It searches repository history, not the
1090 Python/Perl regexps. It searches repository history, not the
1089 working directory. It always prints the revision number in which
1091 working directory. It always prints the revision number in which
1090 a match appears.
1092 a match appears.
1091
1093
1092 By default, grep only prints output for the first revision of a
1094 By default, grep only prints output for the first revision of a
1093 file in which it finds a match. To get it to print every revision
1095 file in which it finds a match. To get it to print every revision
1094 that contains a change in match status ("-" for a match that
1096 that contains a change in match status ("-" for a match that
1095 becomes a non-match, or "+" for a non-match that becomes a match),
1097 becomes a non-match, or "+" for a non-match that becomes a match),
1096 use the --all flag.
1098 use the --all flag.
1097 """
1099 """
1098 reflags = 0
1100 reflags = 0
1099 if opts['ignore_case']:
1101 if opts['ignore_case']:
1100 reflags |= re.I
1102 reflags |= re.I
1101 regexp = re.compile(pattern, reflags)
1103 regexp = re.compile(pattern, reflags)
1102 sep, eol = ':', '\n'
1104 sep, eol = ':', '\n'
1103 if opts['print0']:
1105 if opts['print0']:
1104 sep = eol = '\0'
1106 sep = eol = '\0'
1105
1107
1106 fcache = {}
1108 fcache = {}
1107 def getfile(fn):
1109 def getfile(fn):
1108 if fn not in fcache:
1110 if fn not in fcache:
1109 fcache[fn] = repo.file(fn)
1111 fcache[fn] = repo.file(fn)
1110 return fcache[fn]
1112 return fcache[fn]
1111
1113
1112 def matchlines(body):
1114 def matchlines(body):
1113 begin = 0
1115 begin = 0
1114 linenum = 0
1116 linenum = 0
1115 while True:
1117 while True:
1116 match = regexp.search(body, begin)
1118 match = regexp.search(body, begin)
1117 if not match:
1119 if not match:
1118 break
1120 break
1119 mstart, mend = match.span()
1121 mstart, mend = match.span()
1120 linenum += body.count('\n', begin, mstart) + 1
1122 linenum += body.count('\n', begin, mstart) + 1
1121 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1123 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1122 lend = body.find('\n', mend)
1124 lend = body.find('\n', mend)
1123 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1125 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1124 begin = lend + 1
1126 begin = lend + 1
1125
1127
1126 class linestate(object):
1128 class linestate(object):
1127 def __init__(self, line, linenum, colstart, colend):
1129 def __init__(self, line, linenum, colstart, colend):
1128 self.line = line
1130 self.line = line
1129 self.linenum = linenum
1131 self.linenum = linenum
1130 self.colstart = colstart
1132 self.colstart = colstart
1131 self.colend = colend
1133 self.colend = colend
1132
1134
1133 def __eq__(self, other):
1135 def __eq__(self, other):
1134 return self.line == other.line
1136 return self.line == other.line
1135
1137
1136 matches = {}
1138 matches = {}
1137 copies = {}
1139 copies = {}
1138 def grepbody(fn, rev, body):
1140 def grepbody(fn, rev, body):
1139 matches[rev].setdefault(fn, [])
1141 matches[rev].setdefault(fn, [])
1140 m = matches[rev][fn]
1142 m = matches[rev][fn]
1141 for lnum, cstart, cend, line in matchlines(body):
1143 for lnum, cstart, cend, line in matchlines(body):
1142 s = linestate(line, lnum, cstart, cend)
1144 s = linestate(line, lnum, cstart, cend)
1143 m.append(s)
1145 m.append(s)
1144
1146
1145 def difflinestates(a, b):
1147 def difflinestates(a, b):
1146 sm = difflib.SequenceMatcher(None, a, b)
1148 sm = difflib.SequenceMatcher(None, a, b)
1147 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1149 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1148 if tag == 'insert':
1150 if tag == 'insert':
1149 for i in xrange(blo, bhi):
1151 for i in xrange(blo, bhi):
1150 yield ('+', b[i])
1152 yield ('+', b[i])
1151 elif tag == 'delete':
1153 elif tag == 'delete':
1152 for i in xrange(alo, ahi):
1154 for i in xrange(alo, ahi):
1153 yield ('-', a[i])
1155 yield ('-', a[i])
1154 elif tag == 'replace':
1156 elif tag == 'replace':
1155 for i in xrange(alo, ahi):
1157 for i in xrange(alo, ahi):
1156 yield ('-', a[i])
1158 yield ('-', a[i])
1157 for i in xrange(blo, bhi):
1159 for i in xrange(blo, bhi):
1158 yield ('+', b[i])
1160 yield ('+', b[i])
1159
1161
1160 prev = {}
1162 prev = {}
1161 def display(fn, rev, states, prevstates):
1163 def display(fn, rev, states, prevstates):
1162 found = False
1164 found = False
1163 filerevmatches = {}
1165 filerevmatches = {}
1164 r = prev.get(fn, -1)
1166 r = prev.get(fn, -1)
1165 if opts['all']:
1167 if opts['all']:
1166 iter = difflinestates(states, prevstates)
1168 iter = difflinestates(states, prevstates)
1167 else:
1169 else:
1168 iter = [('', l) for l in prevstates]
1170 iter = [('', l) for l in prevstates]
1169 for change, l in iter:
1171 for change, l in iter:
1170 cols = [fn, str(r)]
1172 cols = [fn, str(r)]
1171 if opts['line_number']:
1173 if opts['line_number']:
1172 cols.append(str(l.linenum))
1174 cols.append(str(l.linenum))
1173 if opts['all']:
1175 if opts['all']:
1174 cols.append(change)
1176 cols.append(change)
1175 if opts['user']:
1177 if opts['user']:
1176 cols.append(ui.shortuser(get(r)[1]))
1178 cols.append(ui.shortuser(get(r)[1]))
1177 if opts['files_with_matches']:
1179 if opts['files_with_matches']:
1178 c = (fn, r)
1180 c = (fn, r)
1179 if c in filerevmatches:
1181 if c in filerevmatches:
1180 continue
1182 continue
1181 filerevmatches[c] = 1
1183 filerevmatches[c] = 1
1182 else:
1184 else:
1183 cols.append(l.line)
1185 cols.append(l.line)
1184 ui.write(sep.join(cols), eol)
1186 ui.write(sep.join(cols), eol)
1185 found = True
1187 found = True
1186 return found
1188 return found
1187
1189
1188 fstate = {}
1190 fstate = {}
1189 skip = {}
1191 skip = {}
1190 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1192 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1191 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1193 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1192 found = False
1194 found = False
1193 follow = opts.get('follow')
1195 follow = opts.get('follow')
1194 for st, rev, fns in changeiter:
1196 for st, rev, fns in changeiter:
1195 if st == 'window':
1197 if st == 'window':
1196 matches.clear()
1198 matches.clear()
1197 elif st == 'add':
1199 elif st == 'add':
1198 mf = repo.changectx(rev).manifest()
1200 mf = repo.changectx(rev).manifest()
1199 matches[rev] = {}
1201 matches[rev] = {}
1200 for fn in fns:
1202 for fn in fns:
1201 if fn in skip:
1203 if fn in skip:
1202 continue
1204 continue
1203 fstate.setdefault(fn, {})
1205 fstate.setdefault(fn, {})
1204 try:
1206 try:
1205 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1207 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1206 if follow:
1208 if follow:
1207 copied = getfile(fn).renamed(mf[fn])
1209 copied = getfile(fn).renamed(mf[fn])
1208 if copied:
1210 if copied:
1209 copies.setdefault(rev, {})[fn] = copied[0]
1211 copies.setdefault(rev, {})[fn] = copied[0]
1210 except KeyError:
1212 except KeyError:
1211 pass
1213 pass
1212 elif st == 'iter':
1214 elif st == 'iter':
1213 states = matches[rev].items()
1215 states = matches[rev].items()
1214 states.sort()
1216 states.sort()
1215 for fn, m in states:
1217 for fn, m in states:
1216 copy = copies.get(rev, {}).get(fn)
1218 copy = copies.get(rev, {}).get(fn)
1217 if fn in skip:
1219 if fn in skip:
1218 if copy:
1220 if copy:
1219 skip[copy] = True
1221 skip[copy] = True
1220 continue
1222 continue
1221 if fn in prev or fstate[fn]:
1223 if fn in prev or fstate[fn]:
1222 r = display(fn, rev, m, fstate[fn])
1224 r = display(fn, rev, m, fstate[fn])
1223 found = found or r
1225 found = found or r
1224 if r and not opts['all']:
1226 if r and not opts['all']:
1225 skip[fn] = True
1227 skip[fn] = True
1226 if copy:
1228 if copy:
1227 skip[copy] = True
1229 skip[copy] = True
1228 fstate[fn] = m
1230 fstate[fn] = m
1229 if copy:
1231 if copy:
1230 fstate[copy] = m
1232 fstate[copy] = m
1231 prev[fn] = rev
1233 prev[fn] = rev
1232
1234
1233 fstate = fstate.items()
1235 fstate = fstate.items()
1234 fstate.sort()
1236 fstate.sort()
1235 for fn, state in fstate:
1237 for fn, state in fstate:
1236 if fn in skip:
1238 if fn in skip:
1237 continue
1239 continue
1238 if fn not in copies.get(prev[fn], {}):
1240 if fn not in copies.get(prev[fn], {}):
1239 found = display(fn, rev, {}, state) or found
1241 found = display(fn, rev, {}, state) or found
1240 return (not found and 1) or 0
1242 return (not found and 1) or 0
1241
1243
1242 def heads(ui, repo, **opts):
1244 def heads(ui, repo, **opts):
1243 """show current repository heads
1245 """show current repository heads
1244
1246
1245 Show all repository head changesets.
1247 Show all repository head changesets.
1246
1248
1247 Repository "heads" are changesets that don't have children
1249 Repository "heads" are changesets that don't have children
1248 changesets. They are where development generally takes place and
1250 changesets. They are where development generally takes place and
1249 are the usual targets for update and merge operations.
1251 are the usual targets for update and merge operations.
1250 """
1252 """
1251 if opts['rev']:
1253 if opts['rev']:
1252 heads = repo.heads(repo.lookup(opts['rev']))
1254 heads = repo.heads(repo.lookup(opts['rev']))
1253 else:
1255 else:
1254 heads = repo.heads()
1256 heads = repo.heads()
1255 displayer = cmdutil.show_changeset(ui, repo, opts)
1257 displayer = cmdutil.show_changeset(ui, repo, opts)
1256 for n in heads:
1258 for n in heads:
1257 displayer.show(changenode=n)
1259 displayer.show(changenode=n)
1258
1260
1259 def help_(ui, name=None, with_version=False):
1261 def help_(ui, name=None, with_version=False):
1260 """show help for a command, extension, or list of commands
1262 """show help for a command, extension, or list of commands
1261
1263
1262 With no arguments, print a list of commands and short help.
1264 With no arguments, print a list of commands and short help.
1263
1265
1264 Given a command name, print help for that command.
1266 Given a command name, print help for that command.
1265
1267
1266 Given an extension name, print help for that extension, and the
1268 Given an extension name, print help for that extension, and the
1267 commands it provides."""
1269 commands it provides."""
1268 option_lists = []
1270 option_lists = []
1269
1271
1270 def helpcmd(name):
1272 def helpcmd(name):
1271 if with_version:
1273 if with_version:
1272 version_(ui)
1274 version_(ui)
1273 ui.write('\n')
1275 ui.write('\n')
1274 aliases, i = findcmd(ui, name)
1276 aliases, i = findcmd(ui, name)
1275 # synopsis
1277 # synopsis
1276 ui.write("%s\n\n" % i[2])
1278 ui.write("%s\n\n" % i[2])
1277
1279
1278 # description
1280 # description
1279 doc = i[0].__doc__
1281 doc = i[0].__doc__
1280 if not doc:
1282 if not doc:
1281 doc = _("(No help text available)")
1283 doc = _("(No help text available)")
1282 if ui.quiet:
1284 if ui.quiet:
1283 doc = doc.splitlines(0)[0]
1285 doc = doc.splitlines(0)[0]
1284 ui.write("%s\n" % doc.rstrip())
1286 ui.write("%s\n" % doc.rstrip())
1285
1287
1286 if not ui.quiet:
1288 if not ui.quiet:
1287 # aliases
1289 # aliases
1288 if len(aliases) > 1:
1290 if len(aliases) > 1:
1289 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1291 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1290
1292
1291 # options
1293 # options
1292 if i[1]:
1294 if i[1]:
1293 option_lists.append(("options", i[1]))
1295 option_lists.append(("options", i[1]))
1294
1296
1295 def helplist(select=None):
1297 def helplist(select=None):
1296 h = {}
1298 h = {}
1297 cmds = {}
1299 cmds = {}
1298 for c, e in table.items():
1300 for c, e in table.items():
1299 f = c.split("|", 1)[0]
1301 f = c.split("|", 1)[0]
1300 if select and not select(f):
1302 if select and not select(f):
1301 continue
1303 continue
1302 if name == "shortlist" and not f.startswith("^"):
1304 if name == "shortlist" and not f.startswith("^"):
1303 continue
1305 continue
1304 f = f.lstrip("^")
1306 f = f.lstrip("^")
1305 if not ui.debugflag and f.startswith("debug"):
1307 if not ui.debugflag and f.startswith("debug"):
1306 continue
1308 continue
1307 doc = e[0].__doc__
1309 doc = e[0].__doc__
1308 if not doc:
1310 if not doc:
1309 doc = _("(No help text available)")
1311 doc = _("(No help text available)")
1310 h[f] = doc.splitlines(0)[0].rstrip()
1312 h[f] = doc.splitlines(0)[0].rstrip()
1311 cmds[f] = c.lstrip("^")
1313 cmds[f] = c.lstrip("^")
1312
1314
1313 fns = h.keys()
1315 fns = h.keys()
1314 fns.sort()
1316 fns.sort()
1315 m = max(map(len, fns))
1317 m = max(map(len, fns))
1316 for f in fns:
1318 for f in fns:
1317 if ui.verbose:
1319 if ui.verbose:
1318 commands = cmds[f].replace("|",", ")
1320 commands = cmds[f].replace("|",", ")
1319 ui.write(" %s:\n %s\n"%(commands, h[f]))
1321 ui.write(" %s:\n %s\n"%(commands, h[f]))
1320 else:
1322 else:
1321 ui.write(' %-*s %s\n' % (m, f, h[f]))
1323 ui.write(' %-*s %s\n' % (m, f, h[f]))
1322
1324
1323 def helptopic(name):
1325 def helptopic(name):
1324 v = None
1326 v = None
1325 for i in help.helptable:
1327 for i in help.helptable:
1326 l = i.split('|')
1328 l = i.split('|')
1327 if name in l:
1329 if name in l:
1328 v = i
1330 v = i
1329 header = l[-1]
1331 header = l[-1]
1330 if not v:
1332 if not v:
1331 raise UnknownCommand(name)
1333 raise UnknownCommand(name)
1332
1334
1333 # description
1335 # description
1334 doc = help.helptable[v]
1336 doc = help.helptable[v]
1335 if not doc:
1337 if not doc:
1336 doc = _("(No help text available)")
1338 doc = _("(No help text available)")
1337 if callable(doc):
1339 if callable(doc):
1338 doc = doc()
1340 doc = doc()
1339
1341
1340 ui.write("%s\n" % header)
1342 ui.write("%s\n" % header)
1341 ui.write("%s\n" % doc.rstrip())
1343 ui.write("%s\n" % doc.rstrip())
1342
1344
1343 def helpext(name):
1345 def helpext(name):
1344 try:
1346 try:
1345 mod = findext(name)
1347 mod = findext(name)
1346 except KeyError:
1348 except KeyError:
1347 raise UnknownCommand(name)
1349 raise UnknownCommand(name)
1348
1350
1349 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1351 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1350 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1352 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1351 for d in doc[1:]:
1353 for d in doc[1:]:
1352 ui.write(d, '\n')
1354 ui.write(d, '\n')
1353
1355
1354 ui.status('\n')
1356 ui.status('\n')
1355
1357
1356 try:
1358 try:
1357 ct = mod.cmdtable
1359 ct = mod.cmdtable
1358 except AttributeError:
1360 except AttributeError:
1359 ui.status(_('no commands defined\n'))
1361 ui.status(_('no commands defined\n'))
1360 return
1362 return
1361
1363
1362 if ui.verbose:
1364 if ui.verbose:
1363 ui.status(_('list of commands:\n\n'))
1365 ui.status(_('list of commands:\n\n'))
1364 else:
1366 else:
1365 ui.status(_('list of commands (use "hg help -v %s" '
1367 ui.status(_('list of commands (use "hg help -v %s" '
1366 'to show aliases and global options):\n\n') % name)
1368 'to show aliases and global options):\n\n') % name)
1367
1369
1368 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1370 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1369 helplist(modcmds.has_key)
1371 helplist(modcmds.has_key)
1370
1372
1371 if name and name != 'shortlist':
1373 if name and name != 'shortlist':
1372 i = None
1374 i = None
1373 for f in (helpcmd, helptopic, helpext):
1375 for f in (helpcmd, helptopic, helpext):
1374 try:
1376 try:
1375 f(name)
1377 f(name)
1376 i = None
1378 i = None
1377 break
1379 break
1378 except UnknownCommand, inst:
1380 except UnknownCommand, inst:
1379 i = inst
1381 i = inst
1380 if i:
1382 if i:
1381 raise i
1383 raise i
1382
1384
1383 else:
1385 else:
1384 # program name
1386 # program name
1385 if ui.verbose or with_version:
1387 if ui.verbose or with_version:
1386 version_(ui)
1388 version_(ui)
1387 else:
1389 else:
1388 ui.status(_("Mercurial Distributed SCM\n"))
1390 ui.status(_("Mercurial Distributed SCM\n"))
1389 ui.status('\n')
1391 ui.status('\n')
1390
1392
1391 # list of commands
1393 # list of commands
1392 if name == "shortlist":
1394 if name == "shortlist":
1393 ui.status(_('basic commands (use "hg help" '
1395 ui.status(_('basic commands (use "hg help" '
1394 'for the full list or option "-v" for details):\n\n'))
1396 'for the full list or option "-v" for details):\n\n'))
1395 elif ui.verbose:
1397 elif ui.verbose:
1396 ui.status(_('list of commands:\n\n'))
1398 ui.status(_('list of commands:\n\n'))
1397 else:
1399 else:
1398 ui.status(_('list of commands (use "hg help -v" '
1400 ui.status(_('list of commands (use "hg help -v" '
1399 'to show aliases and global options):\n\n'))
1401 'to show aliases and global options):\n\n'))
1400
1402
1401 helplist()
1403 helplist()
1402
1404
1403 # global options
1405 # global options
1404 if ui.verbose:
1406 if ui.verbose:
1405 option_lists.append(("global options", globalopts))
1407 option_lists.append(("global options", globalopts))
1406
1408
1407 # list all option lists
1409 # list all option lists
1408 opt_output = []
1410 opt_output = []
1409 for title, options in option_lists:
1411 for title, options in option_lists:
1410 opt_output.append(("\n%s:\n" % title, None))
1412 opt_output.append(("\n%s:\n" % title, None))
1411 for shortopt, longopt, default, desc in options:
1413 for shortopt, longopt, default, desc in options:
1412 if "DEPRECATED" in desc and not ui.verbose: continue
1414 if "DEPRECATED" in desc and not ui.verbose: continue
1413 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1415 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1414 longopt and " --%s" % longopt),
1416 longopt and " --%s" % longopt),
1415 "%s%s" % (desc,
1417 "%s%s" % (desc,
1416 default
1418 default
1417 and _(" (default: %s)") % default
1419 and _(" (default: %s)") % default
1418 or "")))
1420 or "")))
1419
1421
1420 if opt_output:
1422 if opt_output:
1421 opts_len = max([len(line[0]) for line in opt_output if line[1]])
1423 opts_len = max([len(line[0]) for line in opt_output if line[1]])
1422 for first, second in opt_output:
1424 for first, second in opt_output:
1423 if second:
1425 if second:
1424 ui.write(" %-*s %s\n" % (opts_len, first, second))
1426 ui.write(" %-*s %s\n" % (opts_len, first, second))
1425 else:
1427 else:
1426 ui.write("%s\n" % first)
1428 ui.write("%s\n" % first)
1427
1429
1428 def identify(ui, repo):
1430 def identify(ui, repo):
1429 """print information about the working copy
1431 """print information about the working copy
1430
1432
1431 Print a short summary of the current state of the repo.
1433 Print a short summary of the current state of the repo.
1432
1434
1433 This summary identifies the repository state using one or two parent
1435 This summary identifies the repository state using one or two parent
1434 hash identifiers, followed by a "+" if there are uncommitted changes
1436 hash identifiers, followed by a "+" if there are uncommitted changes
1435 in the working directory, followed by a list of tags for this revision.
1437 in the working directory, followed by a list of tags for this revision.
1436 """
1438 """
1437 parents = [p for p in repo.dirstate.parents() if p != nullid]
1439 parents = [p for p in repo.dirstate.parents() if p != nullid]
1438 if not parents:
1440 if not parents:
1439 ui.write(_("unknown\n"))
1441 ui.write(_("unknown\n"))
1440 return
1442 return
1441
1443
1442 hexfunc = ui.debugflag and hex or short
1444 hexfunc = ui.debugflag and hex or short
1443 modified, added, removed, deleted = repo.status()[:4]
1445 modified, added, removed, deleted = repo.status()[:4]
1444 output = ["%s%s" %
1446 output = ["%s%s" %
1445 ('+'.join([hexfunc(parent) for parent in parents]),
1447 ('+'.join([hexfunc(parent) for parent in parents]),
1446 (modified or added or removed or deleted) and "+" or "")]
1448 (modified or added or removed or deleted) and "+" or "")]
1447
1449
1448 if not ui.quiet:
1450 if not ui.quiet:
1449
1451
1450 branch = util.tolocal(repo.workingctx().branch())
1452 branch = util.tolocal(repo.workingctx().branch())
1451 if branch != 'default':
1453 if branch != 'default':
1452 output.append("(%s)" % branch)
1454 output.append("(%s)" % branch)
1453
1455
1454 # multiple tags for a single parent separated by '/'
1456 # multiple tags for a single parent separated by '/'
1455 parenttags = ['/'.join(tags)
1457 parenttags = ['/'.join(tags)
1456 for tags in map(repo.nodetags, parents) if tags]
1458 for tags in map(repo.nodetags, parents) if tags]
1457 # tags for multiple parents separated by ' + '
1459 # tags for multiple parents separated by ' + '
1458 if parenttags:
1460 if parenttags:
1459 output.append(' + '.join(parenttags))
1461 output.append(' + '.join(parenttags))
1460
1462
1461 ui.write("%s\n" % ' '.join(output))
1463 ui.write("%s\n" % ' '.join(output))
1462
1464
1463 def import_(ui, repo, patch1, *patches, **opts):
1465 def import_(ui, repo, patch1, *patches, **opts):
1464 """import an ordered set of patches
1466 """import an ordered set of patches
1465
1467
1466 Import a list of patches and commit them individually.
1468 Import a list of patches and commit them individually.
1467
1469
1468 If there are outstanding changes in the working directory, import
1470 If there are outstanding changes in the working directory, import
1469 will abort unless given the -f flag.
1471 will abort unless given the -f flag.
1470
1472
1471 You can import a patch straight from a mail message. Even patches
1473 You can import a patch straight from a mail message. Even patches
1472 as attachments work (body part must be type text/plain or
1474 as attachments work (body part must be type text/plain or
1473 text/x-patch to be used). From and Subject headers of email
1475 text/x-patch to be used). From and Subject headers of email
1474 message are used as default committer and commit message. All
1476 message are used as default committer and commit message. All
1475 text/plain body parts before first diff are added to commit
1477 text/plain body parts before first diff are added to commit
1476 message.
1478 message.
1477
1479
1478 If imported patch was generated by hg export, user and description
1480 If imported patch was generated by hg export, user and description
1479 from patch override values from message headers and body. Values
1481 from patch override values from message headers and body. Values
1480 given on command line with -m and -u override these.
1482 given on command line with -m and -u override these.
1481
1483
1482 To read a patch from standard input, use patch name "-".
1484 To read a patch from standard input, use patch name "-".
1483 """
1485 """
1484 patches = (patch1,) + patches
1486 patches = (patch1,) + patches
1485
1487
1486 if not opts['force']:
1488 if not opts['force']:
1487 bail_if_changed(repo)
1489 bail_if_changed(repo)
1488
1490
1489 d = opts["base"]
1491 d = opts["base"]
1490 strip = opts["strip"]
1492 strip = opts["strip"]
1491
1493
1492 wlock = repo.wlock()
1494 wlock = repo.wlock()
1493 lock = repo.lock()
1495 lock = repo.lock()
1494
1496
1495 for p in patches:
1497 for p in patches:
1496 pf = os.path.join(d, p)
1498 pf = os.path.join(d, p)
1497
1499
1498 if pf == '-':
1500 if pf == '-':
1499 ui.status(_("applying patch from stdin\n"))
1501 ui.status(_("applying patch from stdin\n"))
1500 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1502 tmpname, message, user, date = patch.extract(ui, sys.stdin)
1501 else:
1503 else:
1502 ui.status(_("applying %s\n") % p)
1504 ui.status(_("applying %s\n") % p)
1503 tmpname, message, user, date = patch.extract(ui, file(pf))
1505 tmpname, message, user, date = patch.extract(ui, file(pf))
1504
1506
1505 if tmpname is None:
1507 if tmpname is None:
1506 raise util.Abort(_('no diffs found'))
1508 raise util.Abort(_('no diffs found'))
1507
1509
1508 try:
1510 try:
1509 cmdline_message = logmessage(opts)
1511 cmdline_message = logmessage(opts)
1510 if cmdline_message:
1512 if cmdline_message:
1511 # pickup the cmdline msg
1513 # pickup the cmdline msg
1512 message = cmdline_message
1514 message = cmdline_message
1513 elif message:
1515 elif message:
1514 # pickup the patch msg
1516 # pickup the patch msg
1515 message = message.strip()
1517 message = message.strip()
1516 else:
1518 else:
1517 # launch the editor
1519 # launch the editor
1518 message = None
1520 message = None
1519 ui.debug(_('message:\n%s\n') % message)
1521 ui.debug(_('message:\n%s\n') % message)
1520
1522
1521 files = {}
1523 files = {}
1522 try:
1524 try:
1523 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1525 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1524 files=files)
1526 files=files)
1525 finally:
1527 finally:
1526 files = patch.updatedir(ui, repo, files, wlock=wlock)
1528 files = patch.updatedir(ui, repo, files, wlock=wlock)
1527 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1529 repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1528 finally:
1530 finally:
1529 os.unlink(tmpname)
1531 os.unlink(tmpname)
1530
1532
1531 def incoming(ui, repo, source="default", **opts):
1533 def incoming(ui, repo, source="default", **opts):
1532 """show new changesets found in source
1534 """show new changesets found in source
1533
1535
1534 Show new changesets found in the specified path/URL or the default
1536 Show new changesets found in the specified path/URL or the default
1535 pull location. These are the changesets that would be pulled if a pull
1537 pull location. These are the changesets that would be pulled if a pull
1536 was requested.
1538 was requested.
1537
1539
1538 For remote repository, using --bundle avoids downloading the changesets
1540 For remote repository, using --bundle avoids downloading the changesets
1539 twice if the incoming is followed by a pull.
1541 twice if the incoming is followed by a pull.
1540
1542
1541 See pull for valid source format details.
1543 See pull for valid source format details.
1542 """
1544 """
1543 source = ui.expandpath(source)
1545 source = ui.expandpath(source)
1544 setremoteconfig(ui, opts)
1546 setremoteconfig(ui, opts)
1545
1547
1546 other = hg.repository(ui, source)
1548 other = hg.repository(ui, source)
1547 ui.status(_('comparing with %s\n') % source)
1549 ui.status(_('comparing with %s\n') % source)
1548 incoming = repo.findincoming(other, force=opts["force"])
1550 incoming = repo.findincoming(other, force=opts["force"])
1549 if not incoming:
1551 if not incoming:
1550 try:
1552 try:
1551 os.unlink(opts["bundle"])
1553 os.unlink(opts["bundle"])
1552 except:
1554 except:
1553 pass
1555 pass
1554 ui.status(_("no changes found\n"))
1556 ui.status(_("no changes found\n"))
1555 return 1
1557 return 1
1556
1558
1557 cleanup = None
1559 cleanup = None
1558 try:
1560 try:
1559 fname = opts["bundle"]
1561 fname = opts["bundle"]
1560 if fname or not other.local():
1562 if fname or not other.local():
1561 # create a bundle (uncompressed if other repo is not local)
1563 # create a bundle (uncompressed if other repo is not local)
1562 cg = other.changegroup(incoming, "incoming")
1564 cg = other.changegroup(incoming, "incoming")
1563 bundletype = other.local() and "HG10BZ" or "HG10UN"
1565 bundletype = other.local() and "HG10BZ" or "HG10UN"
1564 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1566 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1565 # keep written bundle?
1567 # keep written bundle?
1566 if opts["bundle"]:
1568 if opts["bundle"]:
1567 cleanup = None
1569 cleanup = None
1568 if not other.local():
1570 if not other.local():
1569 # use the created uncompressed bundlerepo
1571 # use the created uncompressed bundlerepo
1570 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1572 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1571
1573
1572 revs = None
1574 revs = None
1573 if opts['rev']:
1575 if opts['rev']:
1574 revs = [other.lookup(rev) for rev in opts['rev']]
1576 revs = [other.lookup(rev) for rev in opts['rev']]
1575 o = other.changelog.nodesbetween(incoming, revs)[0]
1577 o = other.changelog.nodesbetween(incoming, revs)[0]
1576 if opts['newest_first']:
1578 if opts['newest_first']:
1577 o.reverse()
1579 o.reverse()
1578 displayer = cmdutil.show_changeset(ui, other, opts)
1580 displayer = cmdutil.show_changeset(ui, other, opts)
1579 for n in o:
1581 for n in o:
1580 parents = [p for p in other.changelog.parents(n) if p != nullid]
1582 parents = [p for p in other.changelog.parents(n) if p != nullid]
1581 if opts['no_merges'] and len(parents) == 2:
1583 if opts['no_merges'] and len(parents) == 2:
1582 continue
1584 continue
1583 displayer.show(changenode=n)
1585 displayer.show(changenode=n)
1584 finally:
1586 finally:
1585 if hasattr(other, 'close'):
1587 if hasattr(other, 'close'):
1586 other.close()
1588 other.close()
1587 if cleanup:
1589 if cleanup:
1588 os.unlink(cleanup)
1590 os.unlink(cleanup)
1589
1591
1590 def init(ui, dest=".", **opts):
1592 def init(ui, dest=".", **opts):
1591 """create a new repository in the given directory
1593 """create a new repository in the given directory
1592
1594
1593 Initialize a new repository in the given directory. If the given
1595 Initialize a new repository in the given directory. If the given
1594 directory does not exist, it is created.
1596 directory does not exist, it is created.
1595
1597
1596 If no directory is given, the current directory is used.
1598 If no directory is given, the current directory is used.
1597
1599
1598 It is possible to specify an ssh:// URL as the destination.
1600 It is possible to specify an ssh:// URL as the destination.
1599 Look at the help text for the pull command for important details
1601 Look at the help text for the pull command for important details
1600 about ssh:// URLs.
1602 about ssh:// URLs.
1601 """
1603 """
1602 setremoteconfig(ui, opts)
1604 setremoteconfig(ui, opts)
1603 hg.repository(ui, dest, create=1)
1605 hg.repository(ui, dest, create=1)
1604
1606
1605 def locate(ui, repo, *pats, **opts):
1607 def locate(ui, repo, *pats, **opts):
1606 """locate files matching specific patterns
1608 """locate files matching specific patterns
1607
1609
1608 Print all files under Mercurial control whose names match the
1610 Print all files under Mercurial control whose names match the
1609 given patterns.
1611 given patterns.
1610
1612
1611 This command searches the entire repository by default. To search
1613 This command searches the entire repository by default. To search
1612 just the current directory and its subdirectories, use "--include .".
1614 just the current directory and its subdirectories, use "--include .".
1613
1615
1614 If no patterns are given to match, this command prints all file
1616 If no patterns are given to match, this command prints all file
1615 names.
1617 names.
1616
1618
1617 If you want to feed the output of this command into the "xargs"
1619 If you want to feed the output of this command into the "xargs"
1618 command, use the "-0" option to both this command and "xargs".
1620 command, use the "-0" option to both this command and "xargs".
1619 This will avoid the problem of "xargs" treating single filenames
1621 This will avoid the problem of "xargs" treating single filenames
1620 that contain white space as multiple filenames.
1622 that contain white space as multiple filenames.
1621 """
1623 """
1622 end = opts['print0'] and '\0' or '\n'
1624 end = opts['print0'] and '\0' or '\n'
1623 rev = opts['rev']
1625 rev = opts['rev']
1624 if rev:
1626 if rev:
1625 node = repo.lookup(rev)
1627 node = repo.lookup(rev)
1626 else:
1628 else:
1627 node = None
1629 node = None
1628
1630
1629 ret = 1
1631 ret = 1
1630 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1632 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1631 default='relglob'):
1633 default='relglob'):
1632 if not node and repo.dirstate.state(abs) == '?':
1634 if not node and repo.dirstate.state(abs) == '?':
1633 continue
1635 continue
1634 if opts['fullpath']:
1636 if opts['fullpath']:
1635 ui.write(os.path.join(repo.root, abs), end)
1637 ui.write(os.path.join(repo.root, abs), end)
1636 else:
1638 else:
1637 ui.write(((pats and rel) or abs), end)
1639 ui.write(((pats and rel) or abs), end)
1638 ret = 0
1640 ret = 0
1639
1641
1640 return ret
1642 return ret
1641
1643
1642 def log(ui, repo, *pats, **opts):
1644 def log(ui, repo, *pats, **opts):
1643 """show revision history of entire repository or files
1645 """show revision history of entire repository or files
1644
1646
1645 Print the revision history of the specified files or the entire
1647 Print the revision history of the specified files or the entire
1646 project.
1648 project.
1647
1649
1648 File history is shown without following rename or copy history of
1650 File history is shown without following rename or copy history of
1649 files. Use -f/--follow with a file name to follow history across
1651 files. Use -f/--follow with a file name to follow history across
1650 renames and copies. --follow without a file name will only show
1652 renames and copies. --follow without a file name will only show
1651 ancestors or descendants of the starting revision. --follow-first
1653 ancestors or descendants of the starting revision. --follow-first
1652 only follows the first parent of merge revisions.
1654 only follows the first parent of merge revisions.
1653
1655
1654 If no revision range is specified, the default is tip:0 unless
1656 If no revision range is specified, the default is tip:0 unless
1655 --follow is set, in which case the working directory parent is
1657 --follow is set, in which case the working directory parent is
1656 used as the starting revision.
1658 used as the starting revision.
1657
1659
1658 By default this command outputs: changeset id and hash, tags,
1660 By default this command outputs: changeset id and hash, tags,
1659 non-trivial parents, user, date and time, and a summary for each
1661 non-trivial parents, user, date and time, and a summary for each
1660 commit. When the -v/--verbose switch is used, the list of changed
1662 commit. When the -v/--verbose switch is used, the list of changed
1661 files and full commit message is shown.
1663 files and full commit message is shown.
1662
1664
1663 NOTE: log -p may generate unexpected diff output for merge
1665 NOTE: log -p may generate unexpected diff output for merge
1664 changesets, as it will compare the merge changeset against its
1666 changesets, as it will compare the merge changeset against its
1665 first parent only. Also, the files: list will only reflect files
1667 first parent only. Also, the files: list will only reflect files
1666 that are different from BOTH parents.
1668 that are different from BOTH parents.
1667
1669
1668 """
1670 """
1669
1671
1670 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1672 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1671 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1673 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1672
1674
1673 if opts['limit']:
1675 if opts['limit']:
1674 try:
1676 try:
1675 limit = int(opts['limit'])
1677 limit = int(opts['limit'])
1676 except ValueError:
1678 except ValueError:
1677 raise util.Abort(_('limit must be a positive integer'))
1679 raise util.Abort(_('limit must be a positive integer'))
1678 if limit <= 0: raise util.Abort(_('limit must be positive'))
1680 if limit <= 0: raise util.Abort(_('limit must be positive'))
1679 else:
1681 else:
1680 limit = sys.maxint
1682 limit = sys.maxint
1681 count = 0
1683 count = 0
1682
1684
1683 if opts['copies'] and opts['rev']:
1685 if opts['copies'] and opts['rev']:
1684 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1686 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1685 else:
1687 else:
1686 endrev = repo.changelog.count()
1688 endrev = repo.changelog.count()
1687 rcache = {}
1689 rcache = {}
1688 ncache = {}
1690 ncache = {}
1689 dcache = []
1691 dcache = []
1690 def getrenamed(fn, rev, man):
1692 def getrenamed(fn, rev, man):
1691 '''looks up all renames for a file (up to endrev) the first
1693 '''looks up all renames for a file (up to endrev) the first
1692 time the file is given. It indexes on the changerev and only
1694 time the file is given. It indexes on the changerev and only
1693 parses the manifest if linkrev != changerev.
1695 parses the manifest if linkrev != changerev.
1694 Returns rename info for fn at changerev rev.'''
1696 Returns rename info for fn at changerev rev.'''
1695 if fn not in rcache:
1697 if fn not in rcache:
1696 rcache[fn] = {}
1698 rcache[fn] = {}
1697 ncache[fn] = {}
1699 ncache[fn] = {}
1698 fl = repo.file(fn)
1700 fl = repo.file(fn)
1699 for i in xrange(fl.count()):
1701 for i in xrange(fl.count()):
1700 node = fl.node(i)
1702 node = fl.node(i)
1701 lr = fl.linkrev(node)
1703 lr = fl.linkrev(node)
1702 renamed = fl.renamed(node)
1704 renamed = fl.renamed(node)
1703 rcache[fn][lr] = renamed
1705 rcache[fn][lr] = renamed
1704 if renamed:
1706 if renamed:
1705 ncache[fn][node] = renamed
1707 ncache[fn][node] = renamed
1706 if lr >= endrev:
1708 if lr >= endrev:
1707 break
1709 break
1708 if rev in rcache[fn]:
1710 if rev in rcache[fn]:
1709 return rcache[fn][rev]
1711 return rcache[fn][rev]
1710 mr = repo.manifest.rev(man)
1712 mr = repo.manifest.rev(man)
1711 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1713 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1712 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1714 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1713 if not dcache or dcache[0] != man:
1715 if not dcache or dcache[0] != man:
1714 dcache[:] = [man, repo.manifest.readdelta(man)]
1716 dcache[:] = [man, repo.manifest.readdelta(man)]
1715 if fn in dcache[1]:
1717 if fn in dcache[1]:
1716 return ncache[fn].get(dcache[1][fn])
1718 return ncache[fn].get(dcache[1][fn])
1717 return None
1719 return None
1718
1720
1719 df = False
1721 df = False
1720 if opts["date"]:
1722 if opts["date"]:
1721 df = util.matchdate(opts["date"])
1723 df = util.matchdate(opts["date"])
1722
1724
1723 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1725 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1724 for st, rev, fns in changeiter:
1726 for st, rev, fns in changeiter:
1725 if st == 'add':
1727 if st == 'add':
1726 changenode = repo.changelog.node(rev)
1728 changenode = repo.changelog.node(rev)
1727 parents = [p for p in repo.changelog.parentrevs(rev)
1729 parents = [p for p in repo.changelog.parentrevs(rev)
1728 if p != nullrev]
1730 if p != nullrev]
1729 if opts['no_merges'] and len(parents) == 2:
1731 if opts['no_merges'] and len(parents) == 2:
1730 continue
1732 continue
1731 if opts['only_merges'] and len(parents) != 2:
1733 if opts['only_merges'] and len(parents) != 2:
1732 continue
1734 continue
1733
1735
1734 if df:
1736 if df:
1735 changes = get(rev)
1737 changes = get(rev)
1736 if not df(changes[2][0]):
1738 if not df(changes[2][0]):
1737 continue
1739 continue
1738
1740
1739 if opts['keyword']:
1741 if opts['keyword']:
1740 changes = get(rev)
1742 changes = get(rev)
1741 miss = 0
1743 miss = 0
1742 for k in [kw.lower() for kw in opts['keyword']]:
1744 for k in [kw.lower() for kw in opts['keyword']]:
1743 if not (k in changes[1].lower() or
1745 if not (k in changes[1].lower() or
1744 k in changes[4].lower() or
1746 k in changes[4].lower() or
1745 k in " ".join(changes[3][:20]).lower()):
1747 k in " ".join(changes[3][:20]).lower()):
1746 miss = 1
1748 miss = 1
1747 break
1749 break
1748 if miss:
1750 if miss:
1749 continue
1751 continue
1750
1752
1751 copies = []
1753 copies = []
1752 if opts.get('copies') and rev:
1754 if opts.get('copies') and rev:
1753 mf = get(rev)[0]
1755 mf = get(rev)[0]
1754 for fn in get(rev)[3]:
1756 for fn in get(rev)[3]:
1755 rename = getrenamed(fn, rev, mf)
1757 rename = getrenamed(fn, rev, mf)
1756 if rename:
1758 if rename:
1757 copies.append((fn, rename[0]))
1759 copies.append((fn, rename[0]))
1758 displayer.show(rev, changenode, copies=copies)
1760 displayer.show(rev, changenode, copies=copies)
1759 elif st == 'iter':
1761 elif st == 'iter':
1760 if count == limit: break
1762 if count == limit: break
1761 if displayer.flush(rev):
1763 if displayer.flush(rev):
1762 count += 1
1764 count += 1
1763
1765
1764 def manifest(ui, repo, rev=None):
1766 def manifest(ui, repo, rev=None):
1765 """output the current or given revision of the project manifest
1767 """output the current or given revision of the project manifest
1766
1768
1767 Print a list of version controlled files for the given revision.
1769 Print a list of version controlled files for the given revision.
1768 If no revision is given, the parent of the working directory is used,
1770 If no revision is given, the parent of the working directory is used,
1769 or tip if no revision is checked out.
1771 or tip if no revision is checked out.
1770
1772
1771 The manifest is the list of files being version controlled. If no revision
1773 The manifest is the list of files being version controlled. If no revision
1772 is given then the first parent of the working directory is used.
1774 is given then the first parent of the working directory is used.
1773
1775
1774 With -v flag, print file permissions. With --debug flag, print
1776 With -v flag, print file permissions. With --debug flag, print
1775 file revision hashes.
1777 file revision hashes.
1776 """
1778 """
1777
1779
1778 m = repo.changectx(rev).manifest()
1780 m = repo.changectx(rev).manifest()
1779 files = m.keys()
1781 files = m.keys()
1780 files.sort()
1782 files.sort()
1781
1783
1782 for f in files:
1784 for f in files:
1783 if ui.debugflag:
1785 if ui.debugflag:
1784 ui.write("%40s " % hex(m[f]))
1786 ui.write("%40s " % hex(m[f]))
1785 if ui.verbose:
1787 if ui.verbose:
1786 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1788 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1787 ui.write("%s\n" % f)
1789 ui.write("%s\n" % f)
1788
1790
1789 def merge(ui, repo, node=None, force=None):
1791 def merge(ui, repo, node=None, force=None):
1790 """merge working directory with another revision
1792 """merge working directory with another revision
1791
1793
1792 Merge the contents of the current working directory and the
1794 Merge the contents of the current working directory and the
1793 requested revision. Files that changed between either parent are
1795 requested revision. Files that changed between either parent are
1794 marked as changed for the next commit and a commit must be
1796 marked as changed for the next commit and a commit must be
1795 performed before any further updates are allowed.
1797 performed before any further updates are allowed.
1796
1798
1797 If no revision is specified, the working directory's parent is a
1799 If no revision is specified, the working directory's parent is a
1798 head revision, and the repository contains exactly one other head,
1800 head revision, and the repository contains exactly one other head,
1799 the other head is merged with by default. Otherwise, an explicit
1801 the other head is merged with by default. Otherwise, an explicit
1800 revision to merge with must be provided.
1802 revision to merge with must be provided.
1801 """
1803 """
1802
1804
1803 if not node:
1805 if not node:
1804 heads = repo.heads()
1806 heads = repo.heads()
1805 if len(heads) > 2:
1807 if len(heads) > 2:
1806 raise util.Abort(_('repo has %d heads - '
1808 raise util.Abort(_('repo has %d heads - '
1807 'please merge with an explicit rev') %
1809 'please merge with an explicit rev') %
1808 len(heads))
1810 len(heads))
1809 if len(heads) == 1:
1811 if len(heads) == 1:
1810 raise util.Abort(_('there is nothing to merge - '
1812 raise util.Abort(_('there is nothing to merge - '
1811 'use "hg update" instead'))
1813 'use "hg update" instead'))
1812 parent = repo.dirstate.parents()[0]
1814 parent = repo.dirstate.parents()[0]
1813 if parent not in heads:
1815 if parent not in heads:
1814 raise util.Abort(_('working dir not at a head rev - '
1816 raise util.Abort(_('working dir not at a head rev - '
1815 'use "hg update" or merge with an explicit rev'))
1817 'use "hg update" or merge with an explicit rev'))
1816 node = parent == heads[0] and heads[-1] or heads[0]
1818 node = parent == heads[0] and heads[-1] or heads[0]
1817 return hg.merge(repo, node, force=force)
1819 return hg.merge(repo, node, force=force)
1818
1820
1819 def outgoing(ui, repo, dest=None, **opts):
1821 def outgoing(ui, repo, dest=None, **opts):
1820 """show changesets not found in destination
1822 """show changesets not found in destination
1821
1823
1822 Show changesets not found in the specified destination repository or
1824 Show changesets not found in the specified destination repository or
1823 the default push location. These are the changesets that would be pushed
1825 the default push location. These are the changesets that would be pushed
1824 if a push was requested.
1826 if a push was requested.
1825
1827
1826 See pull for valid destination format details.
1828 See pull for valid destination format details.
1827 """
1829 """
1828 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1830 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1829 setremoteconfig(ui, opts)
1831 setremoteconfig(ui, opts)
1830 revs = None
1832 revs = None
1831 if opts['rev']:
1833 if opts['rev']:
1832 revs = [repo.lookup(rev) for rev in opts['rev']]
1834 revs = [repo.lookup(rev) for rev in opts['rev']]
1833
1835
1834 other = hg.repository(ui, dest)
1836 other = hg.repository(ui, dest)
1835 ui.status(_('comparing with %s\n') % dest)
1837 ui.status(_('comparing with %s\n') % dest)
1836 o = repo.findoutgoing(other, force=opts['force'])
1838 o = repo.findoutgoing(other, force=opts['force'])
1837 if not o:
1839 if not o:
1838 ui.status(_("no changes found\n"))
1840 ui.status(_("no changes found\n"))
1839 return 1
1841 return 1
1840 o = repo.changelog.nodesbetween(o, revs)[0]
1842 o = repo.changelog.nodesbetween(o, revs)[0]
1841 if opts['newest_first']:
1843 if opts['newest_first']:
1842 o.reverse()
1844 o.reverse()
1843 displayer = cmdutil.show_changeset(ui, repo, opts)
1845 displayer = cmdutil.show_changeset(ui, repo, opts)
1844 for n in o:
1846 for n in o:
1845 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1847 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1846 if opts['no_merges'] and len(parents) == 2:
1848 if opts['no_merges'] and len(parents) == 2:
1847 continue
1849 continue
1848 displayer.show(changenode=n)
1850 displayer.show(changenode=n)
1849
1851
1850 def parents(ui, repo, file_=None, **opts):
1852 def parents(ui, repo, file_=None, **opts):
1851 """show the parents of the working dir or revision
1853 """show the parents of the working dir or revision
1852
1854
1853 Print the working directory's parent revisions.
1855 Print the working directory's parent revisions.
1854 """
1856 """
1855 rev = opts.get('rev')
1857 rev = opts.get('rev')
1856 if rev:
1858 if rev:
1857 if file_:
1859 if file_:
1858 ctx = repo.filectx(file_, changeid=rev)
1860 ctx = repo.filectx(file_, changeid=rev)
1859 else:
1861 else:
1860 ctx = repo.changectx(rev)
1862 ctx = repo.changectx(rev)
1861 p = [cp.node() for cp in ctx.parents()]
1863 p = [cp.node() for cp in ctx.parents()]
1862 else:
1864 else:
1863 p = repo.dirstate.parents()
1865 p = repo.dirstate.parents()
1864
1866
1865 displayer = cmdutil.show_changeset(ui, repo, opts)
1867 displayer = cmdutil.show_changeset(ui, repo, opts)
1866 for n in p:
1868 for n in p:
1867 if n != nullid:
1869 if n != nullid:
1868 displayer.show(changenode=n)
1870 displayer.show(changenode=n)
1869
1871
1870 def paths(ui, repo, search=None):
1872 def paths(ui, repo, search=None):
1871 """show definition of symbolic path names
1873 """show definition of symbolic path names
1872
1874
1873 Show definition of symbolic path name NAME. If no name is given, show
1875 Show definition of symbolic path name NAME. If no name is given, show
1874 definition of available names.
1876 definition of available names.
1875
1877
1876 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1878 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1877 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1879 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1878 """
1880 """
1879 if search:
1881 if search:
1880 for name, path in ui.configitems("paths"):
1882 for name, path in ui.configitems("paths"):
1881 if name == search:
1883 if name == search:
1882 ui.write("%s\n" % path)
1884 ui.write("%s\n" % path)
1883 return
1885 return
1884 ui.warn(_("not found!\n"))
1886 ui.warn(_("not found!\n"))
1885 return 1
1887 return 1
1886 else:
1888 else:
1887 for name, path in ui.configitems("paths"):
1889 for name, path in ui.configitems("paths"):
1888 ui.write("%s = %s\n" % (name, path))
1890 ui.write("%s = %s\n" % (name, path))
1889
1891
1890 def postincoming(ui, repo, modheads, optupdate):
1892 def postincoming(ui, repo, modheads, optupdate):
1891 if modheads == 0:
1893 if modheads == 0:
1892 return
1894 return
1893 if optupdate:
1895 if optupdate:
1894 if modheads == 1:
1896 if modheads == 1:
1895 return hg.update(repo, repo.changelog.tip()) # update
1897 return hg.update(repo, repo.changelog.tip()) # update
1896 else:
1898 else:
1897 ui.status(_("not updating, since new heads added\n"))
1899 ui.status(_("not updating, since new heads added\n"))
1898 if modheads > 1:
1900 if modheads > 1:
1899 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1901 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1900 else:
1902 else:
1901 ui.status(_("(run 'hg update' to get a working copy)\n"))
1903 ui.status(_("(run 'hg update' to get a working copy)\n"))
1902
1904
1903 def pull(ui, repo, source="default", **opts):
1905 def pull(ui, repo, source="default", **opts):
1904 """pull changes from the specified source
1906 """pull changes from the specified source
1905
1907
1906 Pull changes from a remote repository to a local one.
1908 Pull changes from a remote repository to a local one.
1907
1909
1908 This finds all changes from the repository at the specified path
1910 This finds all changes from the repository at the specified path
1909 or URL and adds them to the local repository. By default, this
1911 or URL and adds them to the local repository. By default, this
1910 does not update the copy of the project in the working directory.
1912 does not update the copy of the project in the working directory.
1911
1913
1912 Valid URLs are of the form:
1914 Valid URLs are of the form:
1913
1915
1914 local/filesystem/path (or file://local/filesystem/path)
1916 local/filesystem/path (or file://local/filesystem/path)
1915 http://[user@]host[:port]/[path]
1917 http://[user@]host[:port]/[path]
1916 https://[user@]host[:port]/[path]
1918 https://[user@]host[:port]/[path]
1917 ssh://[user@]host[:port]/[path]
1919 ssh://[user@]host[:port]/[path]
1918 static-http://host[:port]/[path]
1920 static-http://host[:port]/[path]
1919
1921
1920 Paths in the local filesystem can either point to Mercurial
1922 Paths in the local filesystem can either point to Mercurial
1921 repositories or to bundle files (as created by 'hg bundle' or
1923 repositories or to bundle files (as created by 'hg bundle' or
1922 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1924 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1923 allows access to a Mercurial repository where you simply use a web
1925 allows access to a Mercurial repository where you simply use a web
1924 server to publish the .hg directory as static content.
1926 server to publish the .hg directory as static content.
1925
1927
1926 Some notes about using SSH with Mercurial:
1928 Some notes about using SSH with Mercurial:
1927 - SSH requires an accessible shell account on the destination machine
1929 - SSH requires an accessible shell account on the destination machine
1928 and a copy of hg in the remote path or specified with as remotecmd.
1930 and a copy of hg in the remote path or specified with as remotecmd.
1929 - path is relative to the remote user's home directory by default.
1931 - path is relative to the remote user's home directory by default.
1930 Use an extra slash at the start of a path to specify an absolute path:
1932 Use an extra slash at the start of a path to specify an absolute path:
1931 ssh://example.com//tmp/repository
1933 ssh://example.com//tmp/repository
1932 - Mercurial doesn't use its own compression via SSH; the right thing
1934 - Mercurial doesn't use its own compression via SSH; the right thing
1933 to do is to configure it in your ~/.ssh/config, e.g.:
1935 to do is to configure it in your ~/.ssh/config, e.g.:
1934 Host *.mylocalnetwork.example.com
1936 Host *.mylocalnetwork.example.com
1935 Compression no
1937 Compression no
1936 Host *
1938 Host *
1937 Compression yes
1939 Compression yes
1938 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1940 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1939 with the --ssh command line option.
1941 with the --ssh command line option.
1940 """
1942 """
1941 source = ui.expandpath(source)
1943 source = ui.expandpath(source)
1942 setremoteconfig(ui, opts)
1944 setremoteconfig(ui, opts)
1943
1945
1944 other = hg.repository(ui, source)
1946 other = hg.repository(ui, source)
1945 ui.status(_('pulling from %s\n') % (source))
1947 ui.status(_('pulling from %s\n') % (source))
1946 revs = None
1948 revs = None
1947 if opts['rev']:
1949 if opts['rev']:
1948 if 'lookup' in other.capabilities:
1950 if 'lookup' in other.capabilities:
1949 revs = [other.lookup(rev) for rev in opts['rev']]
1951 revs = [other.lookup(rev) for rev in opts['rev']]
1950 else:
1952 else:
1951 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1953 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1952 raise util.Abort(error)
1954 raise util.Abort(error)
1953 modheads = repo.pull(other, heads=revs, force=opts['force'])
1955 modheads = repo.pull(other, heads=revs, force=opts['force'])
1954 return postincoming(ui, repo, modheads, opts['update'])
1956 return postincoming(ui, repo, modheads, opts['update'])
1955
1957
1956 def push(ui, repo, dest=None, **opts):
1958 def push(ui, repo, dest=None, **opts):
1957 """push changes to the specified destination
1959 """push changes to the specified destination
1958
1960
1959 Push changes from the local repository to the given destination.
1961 Push changes from the local repository to the given destination.
1960
1962
1961 This is the symmetrical operation for pull. It helps to move
1963 This is the symmetrical operation for pull. It helps to move
1962 changes from the current repository to a different one. If the
1964 changes from the current repository to a different one. If the
1963 destination is local this is identical to a pull in that directory
1965 destination is local this is identical to a pull in that directory
1964 from the current one.
1966 from the current one.
1965
1967
1966 By default, push will refuse to run if it detects the result would
1968 By default, push will refuse to run if it detects the result would
1967 increase the number of remote heads. This generally indicates the
1969 increase the number of remote heads. This generally indicates the
1968 the client has forgotten to sync and merge before pushing.
1970 the client has forgotten to sync and merge before pushing.
1969
1971
1970 Valid URLs are of the form:
1972 Valid URLs are of the form:
1971
1973
1972 local/filesystem/path (or file://local/filesystem/path)
1974 local/filesystem/path (or file://local/filesystem/path)
1973 ssh://[user@]host[:port]/[path]
1975 ssh://[user@]host[:port]/[path]
1974 http://[user@]host[:port]/[path]
1976 http://[user@]host[:port]/[path]
1975 https://[user@]host[:port]/[path]
1977 https://[user@]host[:port]/[path]
1976
1978
1977 Look at the help text for the pull command for important details
1979 Look at the help text for the pull command for important details
1978 about ssh:// URLs.
1980 about ssh:// URLs.
1979
1981
1980 Pushing to http:// and https:// URLs is only possible, if this
1982 Pushing to http:// and https:// URLs is only possible, if this
1981 feature is explicitly enabled on the remote Mercurial server.
1983 feature is explicitly enabled on the remote Mercurial server.
1982 """
1984 """
1983 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1985 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1984 setremoteconfig(ui, opts)
1986 setremoteconfig(ui, opts)
1985
1987
1986 other = hg.repository(ui, dest)
1988 other = hg.repository(ui, dest)
1987 ui.status('pushing to %s\n' % (dest))
1989 ui.status('pushing to %s\n' % (dest))
1988 revs = None
1990 revs = None
1989 if opts['rev']:
1991 if opts['rev']:
1990 revs = [repo.lookup(rev) for rev in opts['rev']]
1992 revs = [repo.lookup(rev) for rev in opts['rev']]
1991 r = repo.push(other, opts['force'], revs=revs)
1993 r = repo.push(other, opts['force'], revs=revs)
1992 return r == 0
1994 return r == 0
1993
1995
1994 def rawcommit(ui, repo, *pats, **opts):
1996 def rawcommit(ui, repo, *pats, **opts):
1995 """raw commit interface (DEPRECATED)
1997 """raw commit interface (DEPRECATED)
1996
1998
1997 (DEPRECATED)
1999 (DEPRECATED)
1998 Lowlevel commit, for use in helper scripts.
2000 Lowlevel commit, for use in helper scripts.
1999
2001
2000 This command is not intended to be used by normal users, as it is
2002 This command is not intended to be used by normal users, as it is
2001 primarily useful for importing from other SCMs.
2003 primarily useful for importing from other SCMs.
2002
2004
2003 This command is now deprecated and will be removed in a future
2005 This command is now deprecated and will be removed in a future
2004 release, please use debugsetparents and commit instead.
2006 release, please use debugsetparents and commit instead.
2005 """
2007 """
2006
2008
2007 ui.warn(_("(the rawcommit command is deprecated)\n"))
2009 ui.warn(_("(the rawcommit command is deprecated)\n"))
2008
2010
2009 message = logmessage(opts)
2011 message = logmessage(opts)
2010
2012
2011 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2013 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2012 if opts['files']:
2014 if opts['files']:
2013 files += open(opts['files']).read().splitlines()
2015 files += open(opts['files']).read().splitlines()
2014
2016
2015 parents = [repo.lookup(p) for p in opts['parent']]
2017 parents = [repo.lookup(p) for p in opts['parent']]
2016
2018
2017 try:
2019 try:
2018 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2020 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2019 except ValueError, inst:
2021 except ValueError, inst:
2020 raise util.Abort(str(inst))
2022 raise util.Abort(str(inst))
2021
2023
2022 def recover(ui, repo):
2024 def recover(ui, repo):
2023 """roll back an interrupted transaction
2025 """roll back an interrupted transaction
2024
2026
2025 Recover from an interrupted commit or pull.
2027 Recover from an interrupted commit or pull.
2026
2028
2027 This command tries to fix the repository status after an interrupted
2029 This command tries to fix the repository status after an interrupted
2028 operation. It should only be necessary when Mercurial suggests it.
2030 operation. It should only be necessary when Mercurial suggests it.
2029 """
2031 """
2030 if repo.recover():
2032 if repo.recover():
2031 return hg.verify(repo)
2033 return hg.verify(repo)
2032 return 1
2034 return 1
2033
2035
2034 def remove(ui, repo, *pats, **opts):
2036 def remove(ui, repo, *pats, **opts):
2035 """remove the specified files on the next commit
2037 """remove the specified files on the next commit
2036
2038
2037 Schedule the indicated files for removal from the repository.
2039 Schedule the indicated files for removal from the repository.
2038
2040
2039 This only removes files from the current branch, not from the
2041 This only removes files from the current branch, not from the
2040 entire project history. If the files still exist in the working
2042 entire project history. If the files still exist in the working
2041 directory, they will be deleted from it. If invoked with --after,
2043 directory, they will be deleted from it. If invoked with --after,
2042 files that have been manually deleted are marked as removed.
2044 files that have been manually deleted are marked as removed.
2043
2045
2044 This command schedules the files to be removed at the next commit.
2046 This command schedules the files to be removed at the next commit.
2045 To undo a remove before that, see hg revert.
2047 To undo a remove before that, see hg revert.
2046
2048
2047 Modified files and added files are not removed by default. To
2049 Modified files and added files are not removed by default. To
2048 remove them, use the -f/--force option.
2050 remove them, use the -f/--force option.
2049 """
2051 """
2050 names = []
2052 names = []
2051 if not opts['after'] and not pats:
2053 if not opts['after'] and not pats:
2052 raise util.Abort(_('no files specified'))
2054 raise util.Abort(_('no files specified'))
2053 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2055 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2054 exact = dict.fromkeys(files)
2056 exact = dict.fromkeys(files)
2055 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2057 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2056 modified, added, removed, deleted, unknown = mardu
2058 modified, added, removed, deleted, unknown = mardu
2057 remove, forget = [], []
2059 remove, forget = [], []
2058 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2060 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2059 reason = None
2061 reason = None
2060 if abs not in deleted and opts['after']:
2062 if abs not in deleted and opts['after']:
2061 reason = _('is still present')
2063 reason = _('is still present')
2062 elif abs in modified and not opts['force']:
2064 elif abs in modified and not opts['force']:
2063 reason = _('is modified (use -f to force removal)')
2065 reason = _('is modified (use -f to force removal)')
2064 elif abs in added:
2066 elif abs in added:
2065 if opts['force']:
2067 if opts['force']:
2066 forget.append(abs)
2068 forget.append(abs)
2067 continue
2069 continue
2068 reason = _('has been marked for add (use -f to force removal)')
2070 reason = _('has been marked for add (use -f to force removal)')
2069 elif abs in unknown:
2071 elif abs in unknown:
2070 reason = _('is not managed')
2072 reason = _('is not managed')
2071 elif abs in removed:
2073 elif abs in removed:
2072 continue
2074 continue
2073 if reason:
2075 if reason:
2074 if exact:
2076 if exact:
2075 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2077 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2076 else:
2078 else:
2077 if ui.verbose or not exact:
2079 if ui.verbose or not exact:
2078 ui.status(_('removing %s\n') % rel)
2080 ui.status(_('removing %s\n') % rel)
2079 remove.append(abs)
2081 remove.append(abs)
2080 repo.forget(forget)
2082 repo.forget(forget)
2081 repo.remove(remove, unlink=not opts['after'])
2083 repo.remove(remove, unlink=not opts['after'])
2082
2084
2083 def rename(ui, repo, *pats, **opts):
2085 def rename(ui, repo, *pats, **opts):
2084 """rename files; equivalent of copy + remove
2086 """rename files; equivalent of copy + remove
2085
2087
2086 Mark dest as copies of sources; mark sources for deletion. If
2088 Mark dest as copies of sources; mark sources for deletion. If
2087 dest is a directory, copies are put in that directory. If dest is
2089 dest is a directory, copies are put in that directory. If dest is
2088 a file, there can only be one source.
2090 a file, there can only be one source.
2089
2091
2090 By default, this command copies the contents of files as they
2092 By default, this command copies the contents of files as they
2091 stand in the working directory. If invoked with --after, the
2093 stand in the working directory. If invoked with --after, the
2092 operation is recorded, but no copying is performed.
2094 operation is recorded, but no copying is performed.
2093
2095
2094 This command takes effect in the next commit. To undo a rename
2096 This command takes effect in the next commit. To undo a rename
2095 before that, see hg revert.
2097 before that, see hg revert.
2096 """
2098 """
2097 wlock = repo.wlock(0)
2099 wlock = repo.wlock(0)
2098 errs, copied = docopy(ui, repo, pats, opts, wlock)
2100 errs, copied = docopy(ui, repo, pats, opts, wlock)
2099 names = []
2101 names = []
2100 for abs, rel, exact in copied:
2102 for abs, rel, exact in copied:
2101 if ui.verbose or not exact:
2103 if ui.verbose or not exact:
2102 ui.status(_('removing %s\n') % rel)
2104 ui.status(_('removing %s\n') % rel)
2103 names.append(abs)
2105 names.append(abs)
2104 if not opts.get('dry_run'):
2106 if not opts.get('dry_run'):
2105 repo.remove(names, True, wlock)
2107 repo.remove(names, True, wlock)
2106 return errs
2108 return errs
2107
2109
2108 def revert(ui, repo, *pats, **opts):
2110 def revert(ui, repo, *pats, **opts):
2109 """revert files or dirs to their states as of some revision
2111 """revert files or dirs to their states as of some revision
2110
2112
2111 With no revision specified, revert the named files or directories
2113 With no revision specified, revert the named files or directories
2112 to the contents they had in the parent of the working directory.
2114 to the contents they had in the parent of the working directory.
2113 This restores the contents of the affected files to an unmodified
2115 This restores the contents of the affected files to an unmodified
2114 state and unschedules adds, removes, copies, and renames. If the
2116 state and unschedules adds, removes, copies, and renames. If the
2115 working directory has two parents, you must explicitly specify the
2117 working directory has two parents, you must explicitly specify the
2116 revision to revert to.
2118 revision to revert to.
2117
2119
2118 Modified files are saved with a .orig suffix before reverting.
2120 Modified files are saved with a .orig suffix before reverting.
2119 To disable these backups, use --no-backup.
2121 To disable these backups, use --no-backup.
2120
2122
2121 Using the -r option, revert the given files or directories to their
2123 Using the -r option, revert the given files or directories to their
2122 contents as of a specific revision. This can be helpful to "roll
2124 contents as of a specific revision. This can be helpful to "roll
2123 back" some or all of a change that should not have been committed.
2125 back" some or all of a change that should not have been committed.
2124
2126
2125 Revert modifies the working directory. It does not commit any
2127 Revert modifies the working directory. It does not commit any
2126 changes, or change the parent of the working directory. If you
2128 changes, or change the parent of the working directory. If you
2127 revert to a revision other than the parent of the working
2129 revert to a revision other than the parent of the working
2128 directory, the reverted files will thus appear modified
2130 directory, the reverted files will thus appear modified
2129 afterwards.
2131 afterwards.
2130
2132
2131 If a file has been deleted, it is recreated. If the executable
2133 If a file has been deleted, it is recreated. If the executable
2132 mode of a file was changed, it is reset.
2134 mode of a file was changed, it is reset.
2133
2135
2134 If names are given, all files matching the names are reverted.
2136 If names are given, all files matching the names are reverted.
2135
2137
2136 If no arguments are given, no files are reverted.
2138 If no arguments are given, no files are reverted.
2137 """
2139 """
2138
2140
2139 if opts["date"]:
2141 if opts["date"]:
2140 if opts["rev"]:
2142 if opts["rev"]:
2141 raise util.Abort(_("you can't specify a revision and a date"))
2143 raise util.Abort(_("you can't specify a revision and a date"))
2142 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2144 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2143
2145
2144 if not pats and not opts['all']:
2146 if not pats and not opts['all']:
2145 raise util.Abort(_('no files or directories specified; '
2147 raise util.Abort(_('no files or directories specified; '
2146 'use --all to revert the whole repo'))
2148 'use --all to revert the whole repo'))
2147
2149
2148 parent, p2 = repo.dirstate.parents()
2150 parent, p2 = repo.dirstate.parents()
2149 if not opts['rev'] and p2 != nullid:
2151 if not opts['rev'] and p2 != nullid:
2150 raise util.Abort(_('uncommitted merge - please provide a '
2152 raise util.Abort(_('uncommitted merge - please provide a '
2151 'specific revision'))
2153 'specific revision'))
2152 ctx = repo.changectx(opts['rev'])
2154 ctx = repo.changectx(opts['rev'])
2153 node = ctx.node()
2155 node = ctx.node()
2154 mf = ctx.manifest()
2156 mf = ctx.manifest()
2155 if node == parent:
2157 if node == parent:
2156 pmf = mf
2158 pmf = mf
2157 else:
2159 else:
2158 pmf = None
2160 pmf = None
2159
2161
2160 wlock = repo.wlock()
2162 wlock = repo.wlock()
2161
2163
2162 # need all matching names in dirstate and manifest of target rev,
2164 # need all matching names in dirstate and manifest of target rev,
2163 # so have to walk both. do not print errors if files exist in one
2165 # so have to walk both. do not print errors if files exist in one
2164 # but not other.
2166 # but not other.
2165
2167
2166 names = {}
2168 names = {}
2167 target_only = {}
2169 target_only = {}
2168
2170
2169 # walk dirstate.
2171 # walk dirstate.
2170
2172
2171 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2173 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2172 badmatch=mf.has_key):
2174 badmatch=mf.has_key):
2173 names[abs] = (rel, exact)
2175 names[abs] = (rel, exact)
2174 if src == 'b':
2176 if src == 'b':
2175 target_only[abs] = True
2177 target_only[abs] = True
2176
2178
2177 # walk target manifest.
2179 # walk target manifest.
2178
2180
2179 def badmatch(path):
2181 def badmatch(path):
2180 if path in names:
2182 if path in names:
2181 return True
2183 return True
2182 path_ = path + '/'
2184 path_ = path + '/'
2183 for f in names:
2185 for f in names:
2184 if f.startswith(path_):
2186 if f.startswith(path_):
2185 return True
2187 return True
2186 return False
2188 return False
2187
2189
2188 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2190 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2189 badmatch=badmatch):
2191 badmatch=badmatch):
2190 if abs in names or src == 'b':
2192 if abs in names or src == 'b':
2191 continue
2193 continue
2192 names[abs] = (rel, exact)
2194 names[abs] = (rel, exact)
2193 target_only[abs] = True
2195 target_only[abs] = True
2194
2196
2195 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2197 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2196 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2198 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2197
2199
2198 revert = ([], _('reverting %s\n'))
2200 revert = ([], _('reverting %s\n'))
2199 add = ([], _('adding %s\n'))
2201 add = ([], _('adding %s\n'))
2200 remove = ([], _('removing %s\n'))
2202 remove = ([], _('removing %s\n'))
2201 forget = ([], _('forgetting %s\n'))
2203 forget = ([], _('forgetting %s\n'))
2202 undelete = ([], _('undeleting %s\n'))
2204 undelete = ([], _('undeleting %s\n'))
2203 update = {}
2205 update = {}
2204
2206
2205 disptable = (
2207 disptable = (
2206 # dispatch table:
2208 # dispatch table:
2207 # file state
2209 # file state
2208 # action if in target manifest
2210 # action if in target manifest
2209 # action if not in target manifest
2211 # action if not in target manifest
2210 # make backup if in target manifest
2212 # make backup if in target manifest
2211 # make backup if not in target manifest
2213 # make backup if not in target manifest
2212 (modified, revert, remove, True, True),
2214 (modified, revert, remove, True, True),
2213 (added, revert, forget, True, False),
2215 (added, revert, forget, True, False),
2214 (removed, undelete, None, False, False),
2216 (removed, undelete, None, False, False),
2215 (deleted, revert, remove, False, False),
2217 (deleted, revert, remove, False, False),
2216 (unknown, add, None, True, False),
2218 (unknown, add, None, True, False),
2217 (target_only, add, None, False, False),
2219 (target_only, add, None, False, False),
2218 )
2220 )
2219
2221
2220 entries = names.items()
2222 entries = names.items()
2221 entries.sort()
2223 entries.sort()
2222
2224
2223 for abs, (rel, exact) in entries:
2225 for abs, (rel, exact) in entries:
2224 mfentry = mf.get(abs)
2226 mfentry = mf.get(abs)
2225 def handle(xlist, dobackup):
2227 def handle(xlist, dobackup):
2226 xlist[0].append(abs)
2228 xlist[0].append(abs)
2227 update[abs] = 1
2229 update[abs] = 1
2228 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2230 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2229 bakname = "%s.orig" % rel
2231 bakname = "%s.orig" % rel
2230 ui.note(_('saving current version of %s as %s\n') %
2232 ui.note(_('saving current version of %s as %s\n') %
2231 (rel, bakname))
2233 (rel, bakname))
2232 if not opts.get('dry_run'):
2234 if not opts.get('dry_run'):
2233 util.copyfile(rel, bakname)
2235 util.copyfile(rel, bakname)
2234 if ui.verbose or not exact:
2236 if ui.verbose or not exact:
2235 ui.status(xlist[1] % rel)
2237 ui.status(xlist[1] % rel)
2236 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2238 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2237 if abs not in table: continue
2239 if abs not in table: continue
2238 # file has changed in dirstate
2240 # file has changed in dirstate
2239 if mfentry:
2241 if mfentry:
2240 handle(hitlist, backuphit)
2242 handle(hitlist, backuphit)
2241 elif misslist is not None:
2243 elif misslist is not None:
2242 handle(misslist, backupmiss)
2244 handle(misslist, backupmiss)
2243 else:
2245 else:
2244 if exact: ui.warn(_('file not managed: %s\n') % rel)
2246 if exact: ui.warn(_('file not managed: %s\n') % rel)
2245 break
2247 break
2246 else:
2248 else:
2247 # file has not changed in dirstate
2249 # file has not changed in dirstate
2248 if node == parent:
2250 if node == parent:
2249 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2251 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2250 continue
2252 continue
2251 if pmf is None:
2253 if pmf is None:
2252 # only need parent manifest in this unlikely case,
2254 # only need parent manifest in this unlikely case,
2253 # so do not read by default
2255 # so do not read by default
2254 pmf = repo.changectx(parent).manifest()
2256 pmf = repo.changectx(parent).manifest()
2255 if abs in pmf:
2257 if abs in pmf:
2256 if mfentry:
2258 if mfentry:
2257 # if version of file is same in parent and target
2259 # if version of file is same in parent and target
2258 # manifests, do nothing
2260 # manifests, do nothing
2259 if pmf[abs] != mfentry:
2261 if pmf[abs] != mfentry:
2260 handle(revert, False)
2262 handle(revert, False)
2261 else:
2263 else:
2262 handle(remove, False)
2264 handle(remove, False)
2263
2265
2264 if not opts.get('dry_run'):
2266 if not opts.get('dry_run'):
2265 repo.dirstate.forget(forget[0])
2267 repo.dirstate.forget(forget[0])
2266 r = hg.revert(repo, node, update.has_key, wlock)
2268 r = hg.revert(repo, node, update.has_key, wlock)
2267 repo.dirstate.update(add[0], 'a')
2269 repo.dirstate.update(add[0], 'a')
2268 repo.dirstate.update(undelete[0], 'n')
2270 repo.dirstate.update(undelete[0], 'n')
2269 repo.dirstate.update(remove[0], 'r')
2271 repo.dirstate.update(remove[0], 'r')
2270 return r
2272 return r
2271
2273
2272 def rollback(ui, repo):
2274 def rollback(ui, repo):
2273 """roll back the last transaction in this repository
2275 """roll back the last transaction in this repository
2274
2276
2275 Roll back the last transaction in this repository, restoring the
2277 Roll back the last transaction in this repository, restoring the
2276 project to its state prior to the transaction.
2278 project to its state prior to the transaction.
2277
2279
2278 Transactions are used to encapsulate the effects of all commands
2280 Transactions are used to encapsulate the effects of all commands
2279 that create new changesets or propagate existing changesets into a
2281 that create new changesets or propagate existing changesets into a
2280 repository. For example, the following commands are transactional,
2282 repository. For example, the following commands are transactional,
2281 and their effects can be rolled back:
2283 and their effects can be rolled back:
2282
2284
2283 commit
2285 commit
2284 import
2286 import
2285 pull
2287 pull
2286 push (with this repository as destination)
2288 push (with this repository as destination)
2287 unbundle
2289 unbundle
2288
2290
2289 This command should be used with care. There is only one level of
2291 This command should be used with care. There is only one level of
2290 rollback, and there is no way to undo a rollback.
2292 rollback, and there is no way to undo a rollback.
2291
2293
2292 This command is not intended for use on public repositories. Once
2294 This command is not intended for use on public repositories. Once
2293 changes are visible for pull by other users, rolling a transaction
2295 changes are visible for pull by other users, rolling a transaction
2294 back locally is ineffective (someone else may already have pulled
2296 back locally is ineffective (someone else may already have pulled
2295 the changes). Furthermore, a race is possible with readers of the
2297 the changes). Furthermore, a race is possible with readers of the
2296 repository; for example an in-progress pull from the repository
2298 repository; for example an in-progress pull from the repository
2297 may fail if a rollback is performed.
2299 may fail if a rollback is performed.
2298 """
2300 """
2299 repo.rollback()
2301 repo.rollback()
2300
2302
2301 def root(ui, repo):
2303 def root(ui, repo):
2302 """print the root (top) of the current working dir
2304 """print the root (top) of the current working dir
2303
2305
2304 Print the root directory of the current repository.
2306 Print the root directory of the current repository.
2305 """
2307 """
2306 ui.write(repo.root + "\n")
2308 ui.write(repo.root + "\n")
2307
2309
2308 def serve(ui, repo, **opts):
2310 def serve(ui, repo, **opts):
2309 """export the repository via HTTP
2311 """export the repository via HTTP
2310
2312
2311 Start a local HTTP repository browser and pull server.
2313 Start a local HTTP repository browser and pull server.
2312
2314
2313 By default, the server logs accesses to stdout and errors to
2315 By default, the server logs accesses to stdout and errors to
2314 stderr. Use the "-A" and "-E" options to log to files.
2316 stderr. Use the "-A" and "-E" options to log to files.
2315 """
2317 """
2316
2318
2317 if opts["stdio"]:
2319 if opts["stdio"]:
2318 if repo is None:
2320 if repo is None:
2319 raise hg.RepoError(_("There is no Mercurial repository here"
2321 raise hg.RepoError(_("There is no Mercurial repository here"
2320 " (.hg not found)"))
2322 " (.hg not found)"))
2321 s = sshserver.sshserver(ui, repo)
2323 s = sshserver.sshserver(ui, repo)
2322 s.serve_forever()
2324 s.serve_forever()
2323
2325
2324 parentui = ui.parentui or ui
2326 parentui = ui.parentui or ui
2325 optlist = ("name templates style address port ipv6"
2327 optlist = ("name templates style address port ipv6"
2326 " accesslog errorlog webdir_conf")
2328 " accesslog errorlog webdir_conf")
2327 for o in optlist.split():
2329 for o in optlist.split():
2328 if opts[o]:
2330 if opts[o]:
2329 parentui.setconfig("web", o, str(opts[o]))
2331 parentui.setconfig("web", o, str(opts[o]))
2330
2332
2331 if repo is None and not ui.config("web", "webdir_conf"):
2333 if repo is None and not ui.config("web", "webdir_conf"):
2332 raise hg.RepoError(_("There is no Mercurial repository here"
2334 raise hg.RepoError(_("There is no Mercurial repository here"
2333 " (.hg not found)"))
2335 " (.hg not found)"))
2334
2336
2335 if opts['daemon'] and not opts['daemon_pipefds']:
2337 if opts['daemon'] and not opts['daemon_pipefds']:
2336 rfd, wfd = os.pipe()
2338 rfd, wfd = os.pipe()
2337 args = sys.argv[:]
2339 args = sys.argv[:]
2338 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2340 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2339 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2341 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2340 args[0], args)
2342 args[0], args)
2341 os.close(wfd)
2343 os.close(wfd)
2342 os.read(rfd, 1)
2344 os.read(rfd, 1)
2343 os._exit(0)
2345 os._exit(0)
2344
2346
2345 httpd = hgweb.server.create_server(parentui, repo)
2347 httpd = hgweb.server.create_server(parentui, repo)
2346
2348
2347 if ui.verbose:
2349 if ui.verbose:
2348 if httpd.port != 80:
2350 if httpd.port != 80:
2349 ui.status(_('listening at http://%s:%d/\n') %
2351 ui.status(_('listening at http://%s:%d/\n') %
2350 (httpd.addr, httpd.port))
2352 (httpd.addr, httpd.port))
2351 else:
2353 else:
2352 ui.status(_('listening at http://%s/\n') % httpd.addr)
2354 ui.status(_('listening at http://%s/\n') % httpd.addr)
2353
2355
2354 if opts['pid_file']:
2356 if opts['pid_file']:
2355 fp = open(opts['pid_file'], 'w')
2357 fp = open(opts['pid_file'], 'w')
2356 fp.write(str(os.getpid()) + '\n')
2358 fp.write(str(os.getpid()) + '\n')
2357 fp.close()
2359 fp.close()
2358
2360
2359 if opts['daemon_pipefds']:
2361 if opts['daemon_pipefds']:
2360 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2362 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2361 os.close(rfd)
2363 os.close(rfd)
2362 os.write(wfd, 'y')
2364 os.write(wfd, 'y')
2363 os.close(wfd)
2365 os.close(wfd)
2364 sys.stdout.flush()
2366 sys.stdout.flush()
2365 sys.stderr.flush()
2367 sys.stderr.flush()
2366 fd = os.open(util.nulldev, os.O_RDWR)
2368 fd = os.open(util.nulldev, os.O_RDWR)
2367 if fd != 0: os.dup2(fd, 0)
2369 if fd != 0: os.dup2(fd, 0)
2368 if fd != 1: os.dup2(fd, 1)
2370 if fd != 1: os.dup2(fd, 1)
2369 if fd != 2: os.dup2(fd, 2)
2371 if fd != 2: os.dup2(fd, 2)
2370 if fd not in (0, 1, 2): os.close(fd)
2372 if fd not in (0, 1, 2): os.close(fd)
2371
2373
2372 httpd.serve_forever()
2374 httpd.serve_forever()
2373
2375
2374 def status(ui, repo, *pats, **opts):
2376 def status(ui, repo, *pats, **opts):
2375 """show changed files in the working directory
2377 """show changed files in the working directory
2376
2378
2377 Show status of files in the repository. If names are given, only
2379 Show status of files in the repository. If names are given, only
2378 files that match are shown. Files that are clean or ignored, are
2380 files that match are shown. Files that are clean or ignored, are
2379 not listed unless -c (clean), -i (ignored) or -A is given.
2381 not listed unless -c (clean), -i (ignored) or -A is given.
2380
2382
2381 NOTE: status may appear to disagree with diff if permissions have
2383 NOTE: status may appear to disagree with diff if permissions have
2382 changed or a merge has occurred. The standard diff format does not
2384 changed or a merge has occurred. The standard diff format does not
2383 report permission changes and diff only reports changes relative
2385 report permission changes and diff only reports changes relative
2384 to one merge parent.
2386 to one merge parent.
2385
2387
2386 If one revision is given, it is used as the base revision.
2388 If one revision is given, it is used as the base revision.
2387 If two revisions are given, the difference between them is shown.
2389 If two revisions are given, the difference between them is shown.
2388
2390
2389 The codes used to show the status of files are:
2391 The codes used to show the status of files are:
2390 M = modified
2392 M = modified
2391 A = added
2393 A = added
2392 R = removed
2394 R = removed
2393 C = clean
2395 C = clean
2394 ! = deleted, but still tracked
2396 ! = deleted, but still tracked
2395 ? = not tracked
2397 ? = not tracked
2396 I = ignored (not shown by default)
2398 I = ignored (not shown by default)
2397 = the previous added file was copied from here
2399 = the previous added file was copied from here
2398 """
2400 """
2399
2401
2400 all = opts['all']
2402 all = opts['all']
2401 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2403 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2402
2404
2403 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2405 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2404 cwd = (pats and repo.getcwd()) or ''
2406 cwd = (pats and repo.getcwd()) or ''
2405 modified, added, removed, deleted, unknown, ignored, clean = [
2407 modified, added, removed, deleted, unknown, ignored, clean = [
2406 n for n in repo.status(node1=node1, node2=node2, files=files,
2408 n for n in repo.status(node1=node1, node2=node2, files=files,
2407 match=matchfn,
2409 match=matchfn,
2408 list_ignored=all or opts['ignored'],
2410 list_ignored=all or opts['ignored'],
2409 list_clean=all or opts['clean'])]
2411 list_clean=all or opts['clean'])]
2410
2412
2411 changetypes = (('modified', 'M', modified),
2413 changetypes = (('modified', 'M', modified),
2412 ('added', 'A', added),
2414 ('added', 'A', added),
2413 ('removed', 'R', removed),
2415 ('removed', 'R', removed),
2414 ('deleted', '!', deleted),
2416 ('deleted', '!', deleted),
2415 ('unknown', '?', unknown),
2417 ('unknown', '?', unknown),
2416 ('ignored', 'I', ignored))
2418 ('ignored', 'I', ignored))
2417
2419
2418 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2420 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2419
2421
2420 end = opts['print0'] and '\0' or '\n'
2422 end = opts['print0'] and '\0' or '\n'
2421
2423
2422 for opt, char, changes in ([ct for ct in explicit_changetypes
2424 for opt, char, changes in ([ct for ct in explicit_changetypes
2423 if all or opts[ct[0]]]
2425 if all or opts[ct[0]]]
2424 or changetypes):
2426 or changetypes):
2425 if opts['no_status']:
2427 if opts['no_status']:
2426 format = "%%s%s" % end
2428 format = "%%s%s" % end
2427 else:
2429 else:
2428 format = "%s %%s%s" % (char, end)
2430 format = "%s %%s%s" % (char, end)
2429
2431
2430 for f in changes:
2432 for f in changes:
2431 ui.write(format % util.pathto(repo.root, cwd, f))
2433 ui.write(format % util.pathto(repo.root, cwd, f))
2432 if ((all or opts.get('copies')) and not opts.get('no_status')):
2434 if ((all or opts.get('copies')) and not opts.get('no_status')):
2433 copied = repo.dirstate.copied(f)
2435 copied = repo.dirstate.copied(f)
2434 if copied:
2436 if copied:
2435 ui.write(' %s%s' % (util.pathto(repo.root, cwd, copied),
2437 ui.write(' %s%s' % (util.pathto(repo.root, cwd, copied),
2436 end))
2438 end))
2437
2439
2438 def tag(ui, repo, name, rev_=None, **opts):
2440 def tag(ui, repo, name, rev_=None, **opts):
2439 """add a tag for the current or given revision
2441 """add a tag for the current or given revision
2440
2442
2441 Name a particular revision using <name>.
2443 Name a particular revision using <name>.
2442
2444
2443 Tags are used to name particular revisions of the repository and are
2445 Tags are used to name particular revisions of the repository and are
2444 very useful to compare different revision, to go back to significant
2446 very useful to compare different revision, to go back to significant
2445 earlier versions or to mark branch points as releases, etc.
2447 earlier versions or to mark branch points as releases, etc.
2446
2448
2447 If no revision is given, the parent of the working directory is used,
2449 If no revision is given, the parent of the working directory is used,
2448 or tip if no revision is checked out.
2450 or tip if no revision is checked out.
2449
2451
2450 To facilitate version control, distribution, and merging of tags,
2452 To facilitate version control, distribution, and merging of tags,
2451 they are stored as a file named ".hgtags" which is managed
2453 they are stored as a file named ".hgtags" which is managed
2452 similarly to other project files and can be hand-edited if
2454 similarly to other project files and can be hand-edited if
2453 necessary. The file '.hg/localtags' is used for local tags (not
2455 necessary. The file '.hg/localtags' is used for local tags (not
2454 shared among repositories).
2456 shared among repositories).
2455 """
2457 """
2456 if name in ['tip', '.', 'null']:
2458 if name in ['tip', '.', 'null']:
2457 raise util.Abort(_("the name '%s' is reserved") % name)
2459 raise util.Abort(_("the name '%s' is reserved") % name)
2458 if rev_ is not None:
2460 if rev_ is not None:
2459 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2461 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2460 "please use 'hg tag [-r REV] NAME' instead\n"))
2462 "please use 'hg tag [-r REV] NAME' instead\n"))
2461 if opts['rev']:
2463 if opts['rev']:
2462 raise util.Abort(_("use only one form to specify the revision"))
2464 raise util.Abort(_("use only one form to specify the revision"))
2463 if opts['rev'] and opts['remove']:
2465 if opts['rev'] and opts['remove']:
2464 raise util.Abort(_("--rev and --remove are incompatible"))
2466 raise util.Abort(_("--rev and --remove are incompatible"))
2465 if opts['rev']:
2467 if opts['rev']:
2466 rev_ = opts['rev']
2468 rev_ = opts['rev']
2467 message = opts['message']
2469 message = opts['message']
2468 if opts['remove']:
2470 if opts['remove']:
2469 rev_ = nullid
2471 rev_ = nullid
2470 if not message:
2472 if not message:
2471 message = _('Removed tag %s') % name
2473 message = _('Removed tag %s') % name
2472 if not rev_ and repo.dirstate.parents()[1] != nullid:
2474 if not rev_ and repo.dirstate.parents()[1] != nullid:
2473 raise util.Abort(_('uncommitted merge - please provide a '
2475 raise util.Abort(_('uncommitted merge - please provide a '
2474 'specific revision'))
2476 'specific revision'))
2475 r = repo.changectx(rev_).node()
2477 r = repo.changectx(rev_).node()
2476
2478
2477 if not message:
2479 if not message:
2478 message = _('Added tag %s for changeset %s') % (name, short(r))
2480 message = _('Added tag %s for changeset %s') % (name, short(r))
2479
2481
2480 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2482 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2481
2483
2482 def tags(ui, repo):
2484 def tags(ui, repo):
2483 """list repository tags
2485 """list repository tags
2484
2486
2485 List the repository tags.
2487 List the repository tags.
2486
2488
2487 This lists both regular and local tags.
2489 This lists both regular and local tags.
2488 """
2490 """
2489
2491
2490 l = repo.tagslist()
2492 l = repo.tagslist()
2491 l.reverse()
2493 l.reverse()
2492 hexfunc = ui.debugflag and hex or short
2494 hexfunc = ui.debugflag and hex or short
2493 for t, n in l:
2495 for t, n in l:
2494 try:
2496 try:
2495 hn = hexfunc(n)
2497 hn = hexfunc(n)
2496 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2498 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2497 except revlog.LookupError:
2499 except revlog.LookupError:
2498 r = " ?:%s" % hn
2500 r = " ?:%s" % hn
2499 if ui.quiet:
2501 if ui.quiet:
2500 ui.write("%s\n" % t)
2502 ui.write("%s\n" % t)
2501 else:
2503 else:
2502 spaces = " " * (30 - util.locallen(t))
2504 spaces = " " * (30 - util.locallen(t))
2503 ui.write("%s%s %s\n" % (t, spaces, r))
2505 ui.write("%s%s %s\n" % (t, spaces, r))
2504
2506
2505 def tip(ui, repo, **opts):
2507 def tip(ui, repo, **opts):
2506 """show the tip revision
2508 """show the tip revision
2507
2509
2508 Show the tip revision.
2510 Show the tip revision.
2509 """
2511 """
2510 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2512 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2511
2513
2512 def unbundle(ui, repo, fname, **opts):
2514 def unbundle(ui, repo, fname, **opts):
2513 """apply a changegroup file
2515 """apply a changegroup file
2514
2516
2515 Apply a compressed changegroup file generated by the bundle
2517 Apply a compressed changegroup file generated by the bundle
2516 command.
2518 command.
2517 """
2519 """
2518 if os.path.exists(fname):
2520 if os.path.exists(fname):
2519 f = open(fname, "rb")
2521 f = open(fname, "rb")
2520 else:
2522 else:
2521 f = urllib.urlopen(fname)
2523 f = urllib.urlopen(fname)
2522 gen = changegroup.readbundle(f, fname)
2524 gen = changegroup.readbundle(f, fname)
2523 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2525 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2524 return postincoming(ui, repo, modheads, opts['update'])
2526 return postincoming(ui, repo, modheads, opts['update'])
2525
2527
2526 def update(ui, repo, node=None, clean=False, date=None):
2528 def update(ui, repo, node=None, clean=False, date=None):
2527 """update working directory
2529 """update working directory
2528
2530
2529 Update the working directory to the specified revision, or the
2531 Update the working directory to the specified revision, or the
2530 tip of the current branch if none is specified.
2532 tip of the current branch if none is specified.
2531
2533
2532 If there are no outstanding changes in the working directory and
2534 If there are no outstanding changes in the working directory and
2533 there is a linear relationship between the current version and the
2535 there is a linear relationship between the current version and the
2534 requested version, the result is the requested version.
2536 requested version, the result is the requested version.
2535
2537
2536 To merge the working directory with another revision, use the
2538 To merge the working directory with another revision, use the
2537 merge command.
2539 merge command.
2538
2540
2539 By default, update will refuse to run if doing so would require
2541 By default, update will refuse to run if doing so would require
2540 discarding local changes.
2542 discarding local changes.
2541 """
2543 """
2542 if date:
2544 if date:
2543 if node:
2545 if node:
2544 raise util.Abort(_("you can't specify a revision and a date"))
2546 raise util.Abort(_("you can't specify a revision and a date"))
2545 node = cmdutil.finddate(ui, repo, date)
2547 node = cmdutil.finddate(ui, repo, date)
2546
2548
2547 if clean:
2549 if clean:
2548 return hg.clean(repo, node)
2550 return hg.clean(repo, node)
2549 else:
2551 else:
2550 return hg.update(repo, node)
2552 return hg.update(repo, node)
2551
2553
2552 def verify(ui, repo):
2554 def verify(ui, repo):
2553 """verify the integrity of the repository
2555 """verify the integrity of the repository
2554
2556
2555 Verify the integrity of the current repository.
2557 Verify the integrity of the current repository.
2556
2558
2557 This will perform an extensive check of the repository's
2559 This will perform an extensive check of the repository's
2558 integrity, validating the hashes and checksums of each entry in
2560 integrity, validating the hashes and checksums of each entry in
2559 the changelog, manifest, and tracked files, as well as the
2561 the changelog, manifest, and tracked files, as well as the
2560 integrity of their crosslinks and indices.
2562 integrity of their crosslinks and indices.
2561 """
2563 """
2562 return hg.verify(repo)
2564 return hg.verify(repo)
2563
2565
2564 def version_(ui):
2566 def version_(ui):
2565 """output version and copyright information"""
2567 """output version and copyright information"""
2566 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2568 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2567 % version.get_version())
2569 % version.get_version())
2568 ui.status(_(
2570 ui.status(_(
2569 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
2571 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
2570 "This is free software; see the source for copying conditions. "
2572 "This is free software; see the source for copying conditions. "
2571 "There is NO\nwarranty; "
2573 "There is NO\nwarranty; "
2572 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2574 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2573 ))
2575 ))
2574
2576
2575 # Command options and aliases are listed here, alphabetically
2577 # Command options and aliases are listed here, alphabetically
2576
2578
2577 globalopts = [
2579 globalopts = [
2578 ('R', 'repository', '',
2580 ('R', 'repository', '',
2579 _('repository root directory or symbolic path name')),
2581 _('repository root directory or symbolic path name')),
2580 ('', 'cwd', '', _('change working directory')),
2582 ('', 'cwd', '', _('change working directory')),
2581 ('y', 'noninteractive', None,
2583 ('y', 'noninteractive', None,
2582 _('do not prompt, assume \'yes\' for any required answers')),
2584 _('do not prompt, assume \'yes\' for any required answers')),
2583 ('q', 'quiet', None, _('suppress output')),
2585 ('q', 'quiet', None, _('suppress output')),
2584 ('v', 'verbose', None, _('enable additional output')),
2586 ('v', 'verbose', None, _('enable additional output')),
2585 ('', 'config', [], _('set/override config option')),
2587 ('', 'config', [], _('set/override config option')),
2586 ('', 'debug', None, _('enable debugging output')),
2588 ('', 'debug', None, _('enable debugging output')),
2587 ('', 'debugger', None, _('start debugger')),
2589 ('', 'debugger', None, _('start debugger')),
2588 ('', 'encoding', util._encoding, _('set the charset encoding')),
2590 ('', 'encoding', util._encoding, _('set the charset encoding')),
2589 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2591 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2590 ('', 'lsprof', None, _('print improved command execution profile')),
2592 ('', 'lsprof', None, _('print improved command execution profile')),
2591 ('', 'traceback', None, _('print traceback on exception')),
2593 ('', 'traceback', None, _('print traceback on exception')),
2592 ('', 'time', None, _('time how long the command takes')),
2594 ('', 'time', None, _('time how long the command takes')),
2593 ('', 'profile', None, _('print command execution profile')),
2595 ('', 'profile', None, _('print command execution profile')),
2594 ('', 'version', None, _('output version information and exit')),
2596 ('', 'version', None, _('output version information and exit')),
2595 ('h', 'help', None, _('display help and exit')),
2597 ('h', 'help', None, _('display help and exit')),
2596 ]
2598 ]
2597
2599
2598 dryrunopts = [('n', 'dry-run', None,
2600 dryrunopts = [('n', 'dry-run', None,
2599 _('do not perform actions, just print output'))]
2601 _('do not perform actions, just print output'))]
2600
2602
2601 remoteopts = [
2603 remoteopts = [
2602 ('e', 'ssh', '', _('specify ssh command to use')),
2604 ('e', 'ssh', '', _('specify ssh command to use')),
2603 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2605 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2604 ]
2606 ]
2605
2607
2606 walkopts = [
2608 walkopts = [
2607 ('I', 'include', [], _('include names matching the given patterns')),
2609 ('I', 'include', [], _('include names matching the given patterns')),
2608 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2610 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2609 ]
2611 ]
2610
2612
2611 commitopts = [
2613 commitopts = [
2612 ('m', 'message', '', _('use <text> as commit message')),
2614 ('m', 'message', '', _('use <text> as commit message')),
2613 ('l', 'logfile', '', _('read commit message from <file>')),
2615 ('l', 'logfile', '', _('read commit message from <file>')),
2614 ]
2616 ]
2615
2617
2616 table = {
2618 table = {
2617 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2619 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2618 "addremove":
2620 "addremove":
2619 (addremove,
2621 (addremove,
2620 [('s', 'similarity', '',
2622 [('s', 'similarity', '',
2621 _('guess renamed files by similarity (0<=s<=100)')),
2623 _('guess renamed files by similarity (0<=s<=100)')),
2622 ] + walkopts + dryrunopts,
2624 ] + walkopts + dryrunopts,
2623 _('hg addremove [OPTION]... [FILE]...')),
2625 _('hg addremove [OPTION]... [FILE]...')),
2624 "^annotate":
2626 "^annotate":
2625 (annotate,
2627 (annotate,
2626 [('r', 'rev', '', _('annotate the specified revision')),
2628 [('r', 'rev', '', _('annotate the specified revision')),
2627 ('f', 'follow', None, _('follow file copies and renames')),
2629 ('f', 'follow', None, _('follow file copies and renames')),
2628 ('a', 'text', None, _('treat all files as text')),
2630 ('a', 'text', None, _('treat all files as text')),
2629 ('u', 'user', None, _('list the author')),
2631 ('u', 'user', None, _('list the author')),
2630 ('d', 'date', None, _('list the date')),
2632 ('d', 'date', None, _('list the date')),
2631 ('n', 'number', None, _('list the revision number (default)')),
2633 ('n', 'number', None, _('list the revision number (default)')),
2632 ('c', 'changeset', None, _('list the changeset')),
2634 ('c', 'changeset', None, _('list the changeset')),
2633 ] + walkopts,
2635 ] + walkopts,
2634 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2636 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2635 "archive":
2637 "archive":
2636 (archive,
2638 (archive,
2637 [('', 'no-decode', None, _('do not pass files through decoders')),
2639 [('', 'no-decode', None, _('do not pass files through decoders')),
2638 ('p', 'prefix', '', _('directory prefix for files in archive')),
2640 ('p', 'prefix', '', _('directory prefix for files in archive')),
2639 ('r', 'rev', '', _('revision to distribute')),
2641 ('r', 'rev', '', _('revision to distribute')),
2640 ('t', 'type', '', _('type of distribution to create')),
2642 ('t', 'type', '', _('type of distribution to create')),
2641 ] + walkopts,
2643 ] + walkopts,
2642 _('hg archive [OPTION]... DEST')),
2644 _('hg archive [OPTION]... DEST')),
2643 "backout":
2645 "backout":
2644 (backout,
2646 (backout,
2645 [('', 'merge', None,
2647 [('', 'merge', None,
2646 _('merge with old dirstate parent after backout')),
2648 _('merge with old dirstate parent after backout')),
2647 ('d', 'date', '', _('record datecode as commit date')),
2649 ('d', 'date', '', _('record datecode as commit date')),
2648 ('', 'parent', '', _('parent to choose when backing out merge')),
2650 ('', 'parent', '', _('parent to choose when backing out merge')),
2649 ('u', 'user', '', _('record user as committer')),
2651 ('u', 'user', '', _('record user as committer')),
2650 ] + walkopts + commitopts,
2652 ] + walkopts + commitopts,
2651 _('hg backout [OPTION]... REV')),
2653 _('hg backout [OPTION]... REV')),
2652 "branch": (branch,
2654 "branch": (branch,
2653 [('f', 'force', None,
2655 [('f', 'force', None,
2654 _('set branch name even if it shadows an existing branch'))],
2656 _('set branch name even if it shadows an existing branch'))],
2655 _('hg branch [NAME]')),
2657 _('hg branch [NAME]')),
2656 "branches": (branches, [], _('hg branches')),
2658 "branches": (branches, [], _('hg branches')),
2657 "bundle":
2659 "bundle":
2658 (bundle,
2660 (bundle,
2659 [('f', 'force', None,
2661 [('f', 'force', None,
2660 _('run even when remote repository is unrelated')),
2662 _('run even when remote repository is unrelated')),
2661 ('r', 'rev', [],
2663 ('r', 'rev', [],
2662 _('a changeset you would like to bundle')),
2664 _('a changeset you would like to bundle')),
2663 ('', 'base', [],
2665 ('', 'base', [],
2664 _('a base changeset to specify instead of a destination')),
2666 _('a base changeset to specify instead of a destination')),
2665 ] + remoteopts,
2667 ] + remoteopts,
2666 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2668 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2667 "cat":
2669 "cat":
2668 (cat,
2670 (cat,
2669 [('o', 'output', '', _('print output to file with formatted name')),
2671 [('o', 'output', '', _('print output to file with formatted name')),
2670 ('r', 'rev', '', _('print the given revision')),
2672 ('r', 'rev', '', _('print the given revision')),
2671 ] + walkopts,
2673 ] + walkopts,
2672 _('hg cat [OPTION]... FILE...')),
2674 _('hg cat [OPTION]... FILE...')),
2673 "^clone":
2675 "^clone":
2674 (clone,
2676 (clone,
2675 [('U', 'noupdate', None, _('do not update the new working directory')),
2677 [('U', 'noupdate', None, _('do not update the new working directory')),
2676 ('r', 'rev', [],
2678 ('r', 'rev', [],
2677 _('a changeset you would like to have after cloning')),
2679 _('a changeset you would like to have after cloning')),
2678 ('', 'pull', None, _('use pull protocol to copy metadata')),
2680 ('', 'pull', None, _('use pull protocol to copy metadata')),
2679 ('', 'uncompressed', None,
2681 ('', 'uncompressed', None,
2680 _('use uncompressed transfer (fast over LAN)')),
2682 _('use uncompressed transfer (fast over LAN)')),
2681 ] + remoteopts,
2683 ] + remoteopts,
2682 _('hg clone [OPTION]... SOURCE [DEST]')),
2684 _('hg clone [OPTION]... SOURCE [DEST]')),
2683 "^commit|ci":
2685 "^commit|ci":
2684 (commit,
2686 (commit,
2685 [('A', 'addremove', None,
2687 [('A', 'addremove', None,
2686 _('mark new/missing files as added/removed before committing')),
2688 _('mark new/missing files as added/removed before committing')),
2687 ('d', 'date', '', _('record datecode as commit date')),
2689 ('d', 'date', '', _('record datecode as commit date')),
2688 ('u', 'user', '', _('record user as commiter')),
2690 ('u', 'user', '', _('record user as commiter')),
2689 ] + walkopts + commitopts,
2691 ] + walkopts + commitopts,
2690 _('hg commit [OPTION]... [FILE]...')),
2692 _('hg commit [OPTION]... [FILE]...')),
2691 "copy|cp":
2693 "copy|cp":
2692 (copy,
2694 (copy,
2693 [('A', 'after', None, _('record a copy that has already occurred')),
2695 [('A', 'after', None, _('record a copy that has already occurred')),
2694 ('f', 'force', None,
2696 ('f', 'force', None,
2695 _('forcibly copy over an existing managed file')),
2697 _('forcibly copy over an existing managed file')),
2696 ] + walkopts + dryrunopts,
2698 ] + walkopts + dryrunopts,
2697 _('hg copy [OPTION]... [SOURCE]... DEST')),
2699 _('hg copy [OPTION]... [SOURCE]... DEST')),
2698 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2700 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2699 "debugcomplete":
2701 "debugcomplete":
2700 (debugcomplete,
2702 (debugcomplete,
2701 [('o', 'options', None, _('show the command options'))],
2703 [('o', 'options', None, _('show the command options'))],
2702 _('debugcomplete [-o] CMD')),
2704 _('debugcomplete [-o] CMD')),
2703 "debuginstall": (debuginstall, [], _('debuginstall')),
2705 "debuginstall": (debuginstall, [], _('debuginstall')),
2704 "debugrebuildstate":
2706 "debugrebuildstate":
2705 (debugrebuildstate,
2707 (debugrebuildstate,
2706 [('r', 'rev', '', _('revision to rebuild to'))],
2708 [('r', 'rev', '', _('revision to rebuild to'))],
2707 _('debugrebuildstate [-r REV] [REV]')),
2709 _('debugrebuildstate [-r REV] [REV]')),
2708 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2710 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2709 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2711 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2710 "debugstate": (debugstate, [], _('debugstate')),
2712 "debugstate": (debugstate, [], _('debugstate')),
2711 "debugdate":
2713 "debugdate":
2712 (debugdate,
2714 (debugdate,
2713 [('e', 'extended', None, _('try extended date formats'))],
2715 [('e', 'extended', None, _('try extended date formats'))],
2714 _('debugdate [-e] DATE [RANGE]')),
2716 _('debugdate [-e] DATE [RANGE]')),
2715 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2717 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2716 "debugindex": (debugindex, [], _('debugindex FILE')),
2718 "debugindex": (debugindex, [], _('debugindex FILE')),
2717 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2719 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2718 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2720 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2719 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2721 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2720 "^diff":
2722 "^diff":
2721 (diff,
2723 (diff,
2722 [('r', 'rev', [], _('revision')),
2724 [('r', 'rev', [], _('revision')),
2723 ('a', 'text', None, _('treat all files as text')),
2725 ('a', 'text', None, _('treat all files as text')),
2724 ('p', 'show-function', None,
2726 ('p', 'show-function', None,
2725 _('show which function each change is in')),
2727 _('show which function each change is in')),
2726 ('g', 'git', None, _('use git extended diff format')),
2728 ('g', 'git', None, _('use git extended diff format')),
2727 ('', 'nodates', None, _("don't include dates in diff headers")),
2729 ('', 'nodates', None, _("don't include dates in diff headers")),
2728 ('w', 'ignore-all-space', None,
2730 ('w', 'ignore-all-space', None,
2729 _('ignore white space when comparing lines')),
2731 _('ignore white space when comparing lines')),
2730 ('b', 'ignore-space-change', None,
2732 ('b', 'ignore-space-change', None,
2731 _('ignore changes in the amount of white space')),
2733 _('ignore changes in the amount of white space')),
2732 ('B', 'ignore-blank-lines', None,
2734 ('B', 'ignore-blank-lines', None,
2733 _('ignore changes whose lines are all blank')),
2735 _('ignore changes whose lines are all blank')),
2734 ] + walkopts,
2736 ] + walkopts,
2735 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2737 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2736 "^export":
2738 "^export":
2737 (export,
2739 (export,
2738 [('o', 'output', '', _('print output to file with formatted name')),
2740 [('o', 'output', '', _('print output to file with formatted name')),
2739 ('a', 'text', None, _('treat all files as text')),
2741 ('a', 'text', None, _('treat all files as text')),
2740 ('g', 'git', None, _('use git extended diff format')),
2742 ('g', 'git', None, _('use git extended diff format')),
2741 ('', 'nodates', None, _("don't include dates in diff headers")),
2743 ('', 'nodates', None, _("don't include dates in diff headers")),
2742 ('', 'switch-parent', None, _('diff against the second parent'))],
2744 ('', 'switch-parent', None, _('diff against the second parent'))],
2743 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2745 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2744 "grep":
2746 "grep":
2745 (grep,
2747 (grep,
2746 [('0', 'print0', None, _('end fields with NUL')),
2748 [('0', 'print0', None, _('end fields with NUL')),
2747 ('', 'all', None, _('print all revisions that match')),
2749 ('', 'all', None, _('print all revisions that match')),
2748 ('f', 'follow', None,
2750 ('f', 'follow', None,
2749 _('follow changeset history, or file history across copies and renames')),
2751 _('follow changeset history, or file history across copies and renames')),
2750 ('i', 'ignore-case', None, _('ignore case when matching')),
2752 ('i', 'ignore-case', None, _('ignore case when matching')),
2751 ('l', 'files-with-matches', None,
2753 ('l', 'files-with-matches', None,
2752 _('print only filenames and revs that match')),
2754 _('print only filenames and revs that match')),
2753 ('n', 'line-number', None, _('print matching line numbers')),
2755 ('n', 'line-number', None, _('print matching line numbers')),
2754 ('r', 'rev', [], _('search in given revision range')),
2756 ('r', 'rev', [], _('search in given revision range')),
2755 ('u', 'user', None, _('print user who committed change')),
2757 ('u', 'user', None, _('print user who committed change')),
2756 ] + walkopts,
2758 ] + walkopts,
2757 _('hg grep [OPTION]... PATTERN [FILE]...')),
2759 _('hg grep [OPTION]... PATTERN [FILE]...')),
2758 "heads":
2760 "heads":
2759 (heads,
2761 (heads,
2760 [('', 'style', '', _('display using template map file')),
2762 [('', 'style', '', _('display using template map file')),
2761 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2763 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2762 ('', 'template', '', _('display with template'))],
2764 ('', 'template', '', _('display with template'))],
2763 _('hg heads [-r REV]')),
2765 _('hg heads [-r REV]')),
2764 "help": (help_, [], _('hg help [COMMAND]')),
2766 "help": (help_, [], _('hg help [COMMAND]')),
2765 "identify|id": (identify, [], _('hg identify')),
2767 "identify|id": (identify, [], _('hg identify')),
2766 "import|patch":
2768 "import|patch":
2767 (import_,
2769 (import_,
2768 [('p', 'strip', 1,
2770 [('p', 'strip', 1,
2769 _('directory strip option for patch. This has the same\n'
2771 _('directory strip option for patch. This has the same\n'
2770 'meaning as the corresponding patch option')),
2772 'meaning as the corresponding patch option')),
2771 ('b', 'base', '', _('base path')),
2773 ('b', 'base', '', _('base path')),
2772 ('f', 'force', None,
2774 ('f', 'force', None,
2773 _('skip check for outstanding uncommitted changes'))] + commitopts,
2775 _('skip check for outstanding uncommitted changes'))] + commitopts,
2774 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2776 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2775 "incoming|in": (incoming,
2777 "incoming|in": (incoming,
2776 [('M', 'no-merges', None, _('do not show merges')),
2778 [('M', 'no-merges', None, _('do not show merges')),
2777 ('f', 'force', None,
2779 ('f', 'force', None,
2778 _('run even when remote repository is unrelated')),
2780 _('run even when remote repository is unrelated')),
2779 ('', 'style', '', _('display using template map file')),
2781 ('', 'style', '', _('display using template map file')),
2780 ('n', 'newest-first', None, _('show newest record first')),
2782 ('n', 'newest-first', None, _('show newest record first')),
2781 ('', 'bundle', '', _('file to store the bundles into')),
2783 ('', 'bundle', '', _('file to store the bundles into')),
2782 ('p', 'patch', None, _('show patch')),
2784 ('p', 'patch', None, _('show patch')),
2783 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2785 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2784 ('', 'template', '', _('display with template')),
2786 ('', 'template', '', _('display with template')),
2785 ] + remoteopts,
2787 ] + remoteopts,
2786 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2788 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2787 ' [--bundle FILENAME] [SOURCE]')),
2789 ' [--bundle FILENAME] [SOURCE]')),
2788 "^init":
2790 "^init":
2789 (init,
2791 (init,
2790 remoteopts,
2792 remoteopts,
2791 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2793 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2792 "locate":
2794 "locate":
2793 (locate,
2795 (locate,
2794 [('r', 'rev', '', _('search the repository as it stood at rev')),
2796 [('r', 'rev', '', _('search the repository as it stood at rev')),
2795 ('0', 'print0', None,
2797 ('0', 'print0', None,
2796 _('end filenames with NUL, for use with xargs')),
2798 _('end filenames with NUL, for use with xargs')),
2797 ('f', 'fullpath', None,
2799 ('f', 'fullpath', None,
2798 _('print complete paths from the filesystem root')),
2800 _('print complete paths from the filesystem root')),
2799 ] + walkopts,
2801 ] + walkopts,
2800 _('hg locate [OPTION]... [PATTERN]...')),
2802 _('hg locate [OPTION]... [PATTERN]...')),
2801 "^log|history":
2803 "^log|history":
2802 (log,
2804 (log,
2803 [('f', 'follow', None,
2805 [('f', 'follow', None,
2804 _('follow changeset history, or file history across copies and renames')),
2806 _('follow changeset history, or file history across copies and renames')),
2805 ('', 'follow-first', None,
2807 ('', 'follow-first', None,
2806 _('only follow the first parent of merge changesets')),
2808 _('only follow the first parent of merge changesets')),
2807 ('d', 'date', '', _('show revs matching date spec')),
2809 ('d', 'date', '', _('show revs matching date spec')),
2808 ('C', 'copies', None, _('show copied files')),
2810 ('C', 'copies', None, _('show copied files')),
2809 ('k', 'keyword', [], _('search for a keyword')),
2811 ('k', 'keyword', [], _('search for a keyword')),
2810 ('l', 'limit', '', _('limit number of changes displayed')),
2812 ('l', 'limit', '', _('limit number of changes displayed')),
2811 ('r', 'rev', [], _('show the specified revision or range')),
2813 ('r', 'rev', [], _('show the specified revision or range')),
2812 ('', 'removed', None, _('include revs where files were removed')),
2814 ('', 'removed', None, _('include revs where files were removed')),
2813 ('M', 'no-merges', None, _('do not show merges')),
2815 ('M', 'no-merges', None, _('do not show merges')),
2814 ('', 'style', '', _('display using template map file')),
2816 ('', 'style', '', _('display using template map file')),
2815 ('m', 'only-merges', None, _('show only merges')),
2817 ('m', 'only-merges', None, _('show only merges')),
2816 ('p', 'patch', None, _('show patch')),
2818 ('p', 'patch', None, _('show patch')),
2817 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2819 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2818 ('', 'template', '', _('display with template')),
2820 ('', 'template', '', _('display with template')),
2819 ] + walkopts,
2821 ] + walkopts,
2820 _('hg log [OPTION]... [FILE]')),
2822 _('hg log [OPTION]... [FILE]')),
2821 "manifest": (manifest, [], _('hg manifest [REV]')),
2823 "manifest": (manifest, [], _('hg manifest [REV]')),
2822 "^merge":
2824 "^merge":
2823 (merge,
2825 (merge,
2824 [('f', 'force', None, _('force a merge with outstanding changes'))],
2826 [('f', 'force', None, _('force a merge with outstanding changes'))],
2825 _('hg merge [-f] [REV]')),
2827 _('hg merge [-f] [REV]')),
2826 "outgoing|out": (outgoing,
2828 "outgoing|out": (outgoing,
2827 [('M', 'no-merges', None, _('do not show merges')),
2829 [('M', 'no-merges', None, _('do not show merges')),
2828 ('f', 'force', None,
2830 ('f', 'force', None,
2829 _('run even when remote repository is unrelated')),
2831 _('run even when remote repository is unrelated')),
2830 ('p', 'patch', None, _('show patch')),
2832 ('p', 'patch', None, _('show patch')),
2831 ('', 'style', '', _('display using template map file')),
2833 ('', 'style', '', _('display using template map file')),
2832 ('r', 'rev', [], _('a specific revision you would like to push')),
2834 ('r', 'rev', [], _('a specific revision you would like to push')),
2833 ('n', 'newest-first', None, _('show newest record first')),
2835 ('n', 'newest-first', None, _('show newest record first')),
2834 ('', 'template', '', _('display with template')),
2836 ('', 'template', '', _('display with template')),
2835 ] + remoteopts,
2837 ] + remoteopts,
2836 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2838 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2837 "^parents":
2839 "^parents":
2838 (parents,
2840 (parents,
2839 [('r', 'rev', '', _('show parents from the specified rev')),
2841 [('r', 'rev', '', _('show parents from the specified rev')),
2840 ('', 'style', '', _('display using template map file')),
2842 ('', 'style', '', _('display using template map file')),
2841 ('', 'template', '', _('display with template'))],
2843 ('', 'template', '', _('display with template'))],
2842 _('hg parents [-r REV] [FILE]')),
2844 _('hg parents [-r REV] [FILE]')),
2843 "paths": (paths, [], _('hg paths [NAME]')),
2845 "paths": (paths, [], _('hg paths [NAME]')),
2844 "^pull":
2846 "^pull":
2845 (pull,
2847 (pull,
2846 [('u', 'update', None,
2848 [('u', 'update', None,
2847 _('update to new tip if changesets were pulled')),
2849 _('update to new tip if changesets were pulled')),
2848 ('f', 'force', None,
2850 ('f', 'force', None,
2849 _('run even when remote repository is unrelated')),
2851 _('run even when remote repository is unrelated')),
2850 ('r', 'rev', [],
2852 ('r', 'rev', [],
2851 _('a specific revision up to which you would like to pull')),
2853 _('a specific revision up to which you would like to pull')),
2852 ] + remoteopts,
2854 ] + remoteopts,
2853 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
2855 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
2854 "^push":
2856 "^push":
2855 (push,
2857 (push,
2856 [('f', 'force', None, _('force push')),
2858 [('f', 'force', None, _('force push')),
2857 ('r', 'rev', [], _('a specific revision you would like to push')),
2859 ('r', 'rev', [], _('a specific revision you would like to push')),
2858 ] + remoteopts,
2860 ] + remoteopts,
2859 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
2861 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
2860 "debugrawcommit|rawcommit":
2862 "debugrawcommit|rawcommit":
2861 (rawcommit,
2863 (rawcommit,
2862 [('p', 'parent', [], _('parent')),
2864 [('p', 'parent', [], _('parent')),
2863 ('d', 'date', '', _('date code')),
2865 ('d', 'date', '', _('date code')),
2864 ('u', 'user', '', _('user')),
2866 ('u', 'user', '', _('user')),
2865 ('F', 'files', '', _('file list'))
2867 ('F', 'files', '', _('file list'))
2866 ] + commitopts,
2868 ] + commitopts,
2867 _('hg debugrawcommit [OPTION]... [FILE]...')),
2869 _('hg debugrawcommit [OPTION]... [FILE]...')),
2868 "recover": (recover, [], _('hg recover')),
2870 "recover": (recover, [], _('hg recover')),
2869 "^remove|rm":
2871 "^remove|rm":
2870 (remove,
2872 (remove,
2871 [('A', 'after', None, _('record remove that has already occurred')),
2873 [('A', 'after', None, _('record remove that has already occurred')),
2872 ('f', 'force', None, _('remove file even if modified')),
2874 ('f', 'force', None, _('remove file even if modified')),
2873 ] + walkopts,
2875 ] + walkopts,
2874 _('hg remove [OPTION]... FILE...')),
2876 _('hg remove [OPTION]... FILE...')),
2875 "rename|mv":
2877 "rename|mv":
2876 (rename,
2878 (rename,
2877 [('A', 'after', None, _('record a rename that has already occurred')),
2879 [('A', 'after', None, _('record a rename that has already occurred')),
2878 ('f', 'force', None,
2880 ('f', 'force', None,
2879 _('forcibly copy over an existing managed file')),
2881 _('forcibly copy over an existing managed file')),
2880 ] + walkopts + dryrunopts,
2882 ] + walkopts + dryrunopts,
2881 _('hg rename [OPTION]... SOURCE... DEST')),
2883 _('hg rename [OPTION]... SOURCE... DEST')),
2882 "^revert":
2884 "^revert":
2883 (revert,
2885 (revert,
2884 [('a', 'all', None, _('revert all changes when no arguments given')),
2886 [('a', 'all', None, _('revert all changes when no arguments given')),
2885 ('d', 'date', '', _('tipmost revision matching date')),
2887 ('d', 'date', '', _('tipmost revision matching date')),
2886 ('r', 'rev', '', _('revision to revert to')),
2888 ('r', 'rev', '', _('revision to revert to')),
2887 ('', 'no-backup', None, _('do not save backup copies of files')),
2889 ('', 'no-backup', None, _('do not save backup copies of files')),
2888 ] + walkopts + dryrunopts,
2890 ] + walkopts + dryrunopts,
2889 _('hg revert [OPTION]... [-r REV] [NAME]...')),
2891 _('hg revert [OPTION]... [-r REV] [NAME]...')),
2890 "rollback": (rollback, [], _('hg rollback')),
2892 "rollback": (rollback, [], _('hg rollback')),
2891 "root": (root, [], _('hg root')),
2893 "root": (root, [], _('hg root')),
2892 "showconfig|debugconfig":
2894 "showconfig|debugconfig":
2893 (showconfig,
2895 (showconfig,
2894 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2896 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2895 _('showconfig [-u] [NAME]...')),
2897 _('showconfig [-u] [NAME]...')),
2896 "^serve":
2898 "^serve":
2897 (serve,
2899 (serve,
2898 [('A', 'accesslog', '', _('name of access log file to write to')),
2900 [('A', 'accesslog', '', _('name of access log file to write to')),
2899 ('d', 'daemon', None, _('run server in background')),
2901 ('d', 'daemon', None, _('run server in background')),
2900 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2902 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2901 ('E', 'errorlog', '', _('name of error log file to write to')),
2903 ('E', 'errorlog', '', _('name of error log file to write to')),
2902 ('p', 'port', 0, _('port to use (default: 8000)')),
2904 ('p', 'port', 0, _('port to use (default: 8000)')),
2903 ('a', 'address', '', _('address to use')),
2905 ('a', 'address', '', _('address to use')),
2904 ('n', 'name', '',
2906 ('n', 'name', '',
2905 _('name to show in web pages (default: working dir)')),
2907 _('name to show in web pages (default: working dir)')),
2906 ('', 'webdir-conf', '', _('name of the webdir config file'
2908 ('', 'webdir-conf', '', _('name of the webdir config file'
2907 ' (serve more than one repo)')),
2909 ' (serve more than one repo)')),
2908 ('', 'pid-file', '', _('name of file to write process ID to')),
2910 ('', 'pid-file', '', _('name of file to write process ID to')),
2909 ('', 'stdio', None, _('for remote clients')),
2911 ('', 'stdio', None, _('for remote clients')),
2910 ('t', 'templates', '', _('web templates to use')),
2912 ('t', 'templates', '', _('web templates to use')),
2911 ('', 'style', '', _('template style to use')),
2913 ('', 'style', '', _('template style to use')),
2912 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2914 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2913 _('hg serve [OPTION]...')),
2915 _('hg serve [OPTION]...')),
2914 "^status|st":
2916 "^status|st":
2915 (status,
2917 (status,
2916 [('A', 'all', None, _('show status of all files')),
2918 [('A', 'all', None, _('show status of all files')),
2917 ('m', 'modified', None, _('show only modified files')),
2919 ('m', 'modified', None, _('show only modified files')),
2918 ('a', 'added', None, _('show only added files')),
2920 ('a', 'added', None, _('show only added files')),
2919 ('r', 'removed', None, _('show only removed files')),
2921 ('r', 'removed', None, _('show only removed files')),
2920 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2922 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2921 ('c', 'clean', None, _('show only files without changes')),
2923 ('c', 'clean', None, _('show only files without changes')),
2922 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2924 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2923 ('i', 'ignored', None, _('show only ignored files')),
2925 ('i', 'ignored', None, _('show only ignored files')),
2924 ('n', 'no-status', None, _('hide status prefix')),
2926 ('n', 'no-status', None, _('hide status prefix')),
2925 ('C', 'copies', None, _('show source of copied files')),
2927 ('C', 'copies', None, _('show source of copied files')),
2926 ('0', 'print0', None,
2928 ('0', 'print0', None,
2927 _('end filenames with NUL, for use with xargs')),
2929 _('end filenames with NUL, for use with xargs')),
2928 ('', 'rev', [], _('show difference from revision')),
2930 ('', 'rev', [], _('show difference from revision')),
2929 ] + walkopts,
2931 ] + walkopts,
2930 _('hg status [OPTION]... [FILE]...')),
2932 _('hg status [OPTION]... [FILE]...')),
2931 "tag":
2933 "tag":
2932 (tag,
2934 (tag,
2933 [('l', 'local', None, _('make the tag local')),
2935 [('l', 'local', None, _('make the tag local')),
2934 ('m', 'message', '', _('message for tag commit log entry')),
2936 ('m', 'message', '', _('message for tag commit log entry')),
2935 ('d', 'date', '', _('record datecode as commit date')),
2937 ('d', 'date', '', _('record datecode as commit date')),
2936 ('u', 'user', '', _('record user as commiter')),
2938 ('u', 'user', '', _('record user as commiter')),
2937 ('r', 'rev', '', _('revision to tag')),
2939 ('r', 'rev', '', _('revision to tag')),
2938 ('', 'remove', None, _('remove a tag'))],
2940 ('', 'remove', None, _('remove a tag'))],
2939 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2941 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2940 "tags": (tags, [], _('hg tags')),
2942 "tags": (tags, [], _('hg tags')),
2941 "tip":
2943 "tip":
2942 (tip,
2944 (tip,
2943 [('', 'style', '', _('display using template map file')),
2945 [('', 'style', '', _('display using template map file')),
2944 ('p', 'patch', None, _('show patch')),
2946 ('p', 'patch', None, _('show patch')),
2945 ('', 'template', '', _('display with template'))],
2947 ('', 'template', '', _('display with template'))],
2946 _('hg tip [-p]')),
2948 _('hg tip [-p]')),
2947 "unbundle":
2949 "unbundle":
2948 (unbundle,
2950 (unbundle,
2949 [('u', 'update', None,
2951 [('u', 'update', None,
2950 _('update to new tip if changesets were unbundled'))],
2952 _('update to new tip if changesets were unbundled'))],
2951 _('hg unbundle [-u] FILE')),
2953 _('hg unbundle [-u] FILE')),
2952 "^update|up|checkout|co":
2954 "^update|up|checkout|co":
2953 (update,
2955 (update,
2954 [('C', 'clean', None, _('overwrite locally modified files')),
2956 [('C', 'clean', None, _('overwrite locally modified files')),
2955 ('d', 'date', '', _('tipmost revision matching date'))],
2957 ('d', 'date', '', _('tipmost revision matching date'))],
2956 _('hg update [-C] [-d DATE] [REV]')),
2958 _('hg update [-C] [-d DATE] [REV]')),
2957 "verify": (verify, [], _('hg verify')),
2959 "verify": (verify, [], _('hg verify')),
2958 "version": (version_, [], _('hg version')),
2960 "version": (version_, [], _('hg version')),
2959 }
2961 }
2960
2962
2961 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2963 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2962 " debugindex debugindexdot debugdate debuginstall")
2964 " debugindex debugindexdot debugdate debuginstall")
2963 optionalrepo = ("paths serve showconfig")
2965 optionalrepo = ("paths serve showconfig")
2964
2966
2965 def findpossible(ui, cmd):
2967 def findpossible(ui, cmd):
2966 """
2968 """
2967 Return cmd -> (aliases, command table entry)
2969 Return cmd -> (aliases, command table entry)
2968 for each matching command.
2970 for each matching command.
2969 Return debug commands (or their aliases) only if no normal command matches.
2971 Return debug commands (or their aliases) only if no normal command matches.
2970 """
2972 """
2971 choice = {}
2973 choice = {}
2972 debugchoice = {}
2974 debugchoice = {}
2973 for e in table.keys():
2975 for e in table.keys():
2974 aliases = e.lstrip("^").split("|")
2976 aliases = e.lstrip("^").split("|")
2975 found = None
2977 found = None
2976 if cmd in aliases:
2978 if cmd in aliases:
2977 found = cmd
2979 found = cmd
2978 elif not ui.config("ui", "strict"):
2980 elif not ui.config("ui", "strict"):
2979 for a in aliases:
2981 for a in aliases:
2980 if a.startswith(cmd):
2982 if a.startswith(cmd):
2981 found = a
2983 found = a
2982 break
2984 break
2983 if found is not None:
2985 if found is not None:
2984 if aliases[0].startswith("debug") or found.startswith("debug"):
2986 if aliases[0].startswith("debug") or found.startswith("debug"):
2985 debugchoice[found] = (aliases, table[e])
2987 debugchoice[found] = (aliases, table[e])
2986 else:
2988 else:
2987 choice[found] = (aliases, table[e])
2989 choice[found] = (aliases, table[e])
2988
2990
2989 if not choice and debugchoice:
2991 if not choice and debugchoice:
2990 choice = debugchoice
2992 choice = debugchoice
2991
2993
2992 return choice
2994 return choice
2993
2995
2994 def findcmd(ui, cmd):
2996 def findcmd(ui, cmd):
2995 """Return (aliases, command table entry) for command string."""
2997 """Return (aliases, command table entry) for command string."""
2996 choice = findpossible(ui, cmd)
2998 choice = findpossible(ui, cmd)
2997
2999
2998 if choice.has_key(cmd):
3000 if choice.has_key(cmd):
2999 return choice[cmd]
3001 return choice[cmd]
3000
3002
3001 if len(choice) > 1:
3003 if len(choice) > 1:
3002 clist = choice.keys()
3004 clist = choice.keys()
3003 clist.sort()
3005 clist.sort()
3004 raise AmbiguousCommand(cmd, clist)
3006 raise AmbiguousCommand(cmd, clist)
3005
3007
3006 if choice:
3008 if choice:
3007 return choice.values()[0]
3009 return choice.values()[0]
3008
3010
3009 raise UnknownCommand(cmd)
3011 raise UnknownCommand(cmd)
3010
3012
3011 def catchterm(*args):
3013 def catchterm(*args):
3012 raise util.SignalInterrupt
3014 raise util.SignalInterrupt
3013
3015
3014 def run():
3016 def run():
3015 sys.exit(dispatch(sys.argv[1:]))
3017 sys.exit(dispatch(sys.argv[1:]))
3016
3018
3017 class ParseError(Exception):
3019 class ParseError(Exception):
3018 """Exception raised on errors in parsing the command line."""
3020 """Exception raised on errors in parsing the command line."""
3019
3021
3020 def parse(ui, args):
3022 def parse(ui, args):
3021 options = {}
3023 options = {}
3022 cmdoptions = {}
3024 cmdoptions = {}
3023
3025
3024 try:
3026 try:
3025 args = fancyopts.fancyopts(args, globalopts, options)
3027 args = fancyopts.fancyopts(args, globalopts, options)
3026 except fancyopts.getopt.GetoptError, inst:
3028 except fancyopts.getopt.GetoptError, inst:
3027 raise ParseError(None, inst)
3029 raise ParseError(None, inst)
3028
3030
3029 if args:
3031 if args:
3030 cmd, args = args[0], args[1:]
3032 cmd, args = args[0], args[1:]
3031 aliases, i = findcmd(ui, cmd)
3033 aliases, i = findcmd(ui, cmd)
3032 cmd = aliases[0]
3034 cmd = aliases[0]
3033 defaults = ui.config("defaults", cmd)
3035 defaults = ui.config("defaults", cmd)
3034 if defaults:
3036 if defaults:
3035 args = shlex.split(defaults) + args
3037 args = shlex.split(defaults) + args
3036 c = list(i[1])
3038 c = list(i[1])
3037 else:
3039 else:
3038 cmd = None
3040 cmd = None
3039 c = []
3041 c = []
3040
3042
3041 # combine global options into local
3043 # combine global options into local
3042 for o in globalopts:
3044 for o in globalopts:
3043 c.append((o[0], o[1], options[o[1]], o[3]))
3045 c.append((o[0], o[1], options[o[1]], o[3]))
3044
3046
3045 try:
3047 try:
3046 args = fancyopts.fancyopts(args, c, cmdoptions)
3048 args = fancyopts.fancyopts(args, c, cmdoptions)
3047 except fancyopts.getopt.GetoptError, inst:
3049 except fancyopts.getopt.GetoptError, inst:
3048 raise ParseError(cmd, inst)
3050 raise ParseError(cmd, inst)
3049
3051
3050 # separate global options back out
3052 # separate global options back out
3051 for o in globalopts:
3053 for o in globalopts:
3052 n = o[1]
3054 n = o[1]
3053 options[n] = cmdoptions[n]
3055 options[n] = cmdoptions[n]
3054 del cmdoptions[n]
3056 del cmdoptions[n]
3055
3057
3056 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3058 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3057
3059
3058 external = {}
3060 external = {}
3059
3061
3060 def findext(name):
3062 def findext(name):
3061 '''return module with given extension name'''
3063 '''return module with given extension name'''
3062 try:
3064 try:
3063 return sys.modules[external[name]]
3065 return sys.modules[external[name]]
3064 except KeyError:
3066 except KeyError:
3065 for k, v in external.iteritems():
3067 for k, v in external.iteritems():
3066 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3068 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3067 return sys.modules[v]
3069 return sys.modules[v]
3068 raise KeyError(name)
3070 raise KeyError(name)
3069
3071
3070 def load_extensions(ui):
3072 def load_extensions(ui):
3071 added = []
3073 added = []
3072 for ext_name, load_from_name in ui.extensions():
3074 for ext_name, load_from_name in ui.extensions():
3073 if ext_name in external:
3075 if ext_name in external:
3074 continue
3076 continue
3075 try:
3077 try:
3076 if load_from_name:
3078 if load_from_name:
3077 # the module will be loaded in sys.modules
3079 # the module will be loaded in sys.modules
3078 # choose an unique name so that it doesn't
3080 # choose an unique name so that it doesn't
3079 # conflicts with other modules
3081 # conflicts with other modules
3080 module_name = "hgext_%s" % ext_name.replace('.', '_')
3082 module_name = "hgext_%s" % ext_name.replace('.', '_')
3081 mod = imp.load_source(module_name, load_from_name)
3083 mod = imp.load_source(module_name, load_from_name)
3082 else:
3084 else:
3083 def importh(name):
3085 def importh(name):
3084 mod = __import__(name)
3086 mod = __import__(name)
3085 components = name.split('.')
3087 components = name.split('.')
3086 for comp in components[1:]:
3088 for comp in components[1:]:
3087 mod = getattr(mod, comp)
3089 mod = getattr(mod, comp)
3088 return mod
3090 return mod
3089 try:
3091 try:
3090 mod = importh("hgext.%s" % ext_name)
3092 mod = importh("hgext.%s" % ext_name)
3091 except ImportError:
3093 except ImportError:
3092 mod = importh(ext_name)
3094 mod = importh(ext_name)
3093 external[ext_name] = mod.__name__
3095 external[ext_name] = mod.__name__
3094 added.append((mod, ext_name))
3096 added.append((mod, ext_name))
3095 except (util.SignalInterrupt, KeyboardInterrupt):
3097 except (util.SignalInterrupt, KeyboardInterrupt):
3096 raise
3098 raise
3097 except Exception, inst:
3099 except Exception, inst:
3098 ui.warn(_("*** failed to import extension %s: %s\n") %
3100 ui.warn(_("*** failed to import extension %s: %s\n") %
3099 (ext_name, inst))
3101 (ext_name, inst))
3100 if ui.print_exc():
3102 if ui.print_exc():
3101 return 1
3103 return 1
3102
3104
3103 for mod, name in added:
3105 for mod, name in added:
3104 uisetup = getattr(mod, 'uisetup', None)
3106 uisetup = getattr(mod, 'uisetup', None)
3105 if uisetup:
3107 if uisetup:
3106 uisetup(ui)
3108 uisetup(ui)
3107 reposetup = getattr(mod, 'reposetup', None)
3109 reposetup = getattr(mod, 'reposetup', None)
3108 if reposetup:
3110 if reposetup:
3109 hg.repo_setup_hooks.append(reposetup)
3111 hg.repo_setup_hooks.append(reposetup)
3110 cmdtable = getattr(mod, 'cmdtable', {})
3112 cmdtable = getattr(mod, 'cmdtable', {})
3111 overrides = [cmd for cmd in cmdtable if cmd in table]
3113 overrides = [cmd for cmd in cmdtable if cmd in table]
3112 if overrides:
3114 if overrides:
3113 ui.warn(_("extension '%s' overrides commands: %s\n")
3115 ui.warn(_("extension '%s' overrides commands: %s\n")
3114 % (name, " ".join(overrides)))
3116 % (name, " ".join(overrides)))
3115 table.update(cmdtable)
3117 table.update(cmdtable)
3116
3118
3117 def parseconfig(config):
3119 def parseconfig(config):
3118 """parse the --config options from the command line"""
3120 """parse the --config options from the command line"""
3119 parsed = []
3121 parsed = []
3120 for cfg in config:
3122 for cfg in config:
3121 try:
3123 try:
3122 name, value = cfg.split('=', 1)
3124 name, value = cfg.split('=', 1)
3123 section, name = name.split('.', 1)
3125 section, name = name.split('.', 1)
3124 if not section or not name:
3126 if not section or not name:
3125 raise IndexError
3127 raise IndexError
3126 parsed.append((section, name, value))
3128 parsed.append((section, name, value))
3127 except (IndexError, ValueError):
3129 except (IndexError, ValueError):
3128 raise util.Abort(_('malformed --config option: %s') % cfg)
3130 raise util.Abort(_('malformed --config option: %s') % cfg)
3129 return parsed
3131 return parsed
3130
3132
3131 def dispatch(args):
3133 def dispatch(args):
3132 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3134 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3133 num = getattr(signal, name, None)
3135 num = getattr(signal, name, None)
3134 if num: signal.signal(num, catchterm)
3136 if num: signal.signal(num, catchterm)
3135
3137
3136 try:
3138 try:
3137 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3139 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3138 except util.Abort, inst:
3140 except util.Abort, inst:
3139 sys.stderr.write(_("abort: %s\n") % inst)
3141 sys.stderr.write(_("abort: %s\n") % inst)
3140 return -1
3142 return -1
3141
3143
3142 load_extensions(u)
3144 load_extensions(u)
3143 u.addreadhook(load_extensions)
3145 u.addreadhook(load_extensions)
3144
3146
3145 try:
3147 try:
3146 cmd, func, args, options, cmdoptions = parse(u, args)
3148 cmd, func, args, options, cmdoptions = parse(u, args)
3147 if options["encoding"]:
3149 if options["encoding"]:
3148 util._encoding = options["encoding"]
3150 util._encoding = options["encoding"]
3149 if options["encodingmode"]:
3151 if options["encodingmode"]:
3150 util._encodingmode = options["encodingmode"]
3152 util._encodingmode = options["encodingmode"]
3151 if options["time"]:
3153 if options["time"]:
3152 def get_times():
3154 def get_times():
3153 t = os.times()
3155 t = os.times()
3154 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3156 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3155 t = (t[0], t[1], t[2], t[3], time.clock())
3157 t = (t[0], t[1], t[2], t[3], time.clock())
3156 return t
3158 return t
3157 s = get_times()
3159 s = get_times()
3158 def print_time():
3160 def print_time():
3159 t = get_times()
3161 t = get_times()
3160 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3162 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3161 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3163 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3162 atexit.register(print_time)
3164 atexit.register(print_time)
3163
3165
3164 # enter the debugger before command execution
3166 # enter the debugger before command execution
3165 if options['debugger']:
3167 if options['debugger']:
3166 pdb.set_trace()
3168 pdb.set_trace()
3167
3169
3168 try:
3170 try:
3169 if options['cwd']:
3171 if options['cwd']:
3170 os.chdir(options['cwd'])
3172 os.chdir(options['cwd'])
3171
3173
3172 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3174 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3173 not options["noninteractive"], options["traceback"],
3175 not options["noninteractive"], options["traceback"],
3174 parseconfig(options["config"]))
3176 parseconfig(options["config"]))
3175
3177
3176 path = u.expandpath(options["repository"]) or ""
3178 path = u.expandpath(options["repository"]) or ""
3177 repo = path and hg.repository(u, path=path) or None
3179 repo = path and hg.repository(u, path=path) or None
3178 if repo and not repo.local():
3180 if repo and not repo.local():
3179 raise util.Abort(_("repository '%s' is not local") % path)
3181 raise util.Abort(_("repository '%s' is not local") % path)
3180
3182
3181 if options['help']:
3183 if options['help']:
3182 return help_(u, cmd, options['version'])
3184 return help_(u, cmd, options['version'])
3183 elif options['version']:
3185 elif options['version']:
3184 return version_(u)
3186 return version_(u)
3185 elif not cmd:
3187 elif not cmd:
3186 return help_(u, 'shortlist')
3188 return help_(u, 'shortlist')
3187
3189
3188 if cmd not in norepo.split():
3190 if cmd not in norepo.split():
3189 try:
3191 try:
3190 if not repo:
3192 if not repo:
3191 repo = hg.repository(u, path=path)
3193 repo = hg.repository(u, path=path)
3192 u = repo.ui
3194 u = repo.ui
3193 except hg.RepoError:
3195 except hg.RepoError:
3194 if cmd not in optionalrepo.split():
3196 if cmd not in optionalrepo.split():
3195 raise
3197 raise
3196 d = lambda: func(u, repo, *args, **cmdoptions)
3198 d = lambda: func(u, repo, *args, **cmdoptions)
3197 else:
3199 else:
3198 d = lambda: func(u, *args, **cmdoptions)
3200 d = lambda: func(u, *args, **cmdoptions)
3199
3201
3200 try:
3202 try:
3201 if options['profile']:
3203 if options['profile']:
3202 import hotshot, hotshot.stats
3204 import hotshot, hotshot.stats
3203 prof = hotshot.Profile("hg.prof")
3205 prof = hotshot.Profile("hg.prof")
3204 try:
3206 try:
3205 try:
3207 try:
3206 return prof.runcall(d)
3208 return prof.runcall(d)
3207 except:
3209 except:
3208 try:
3210 try:
3209 u.warn(_('exception raised - generating '
3211 u.warn(_('exception raised - generating '
3210 'profile anyway\n'))
3212 'profile anyway\n'))
3211 except:
3213 except:
3212 pass
3214 pass
3213 raise
3215 raise
3214 finally:
3216 finally:
3215 prof.close()
3217 prof.close()
3216 stats = hotshot.stats.load("hg.prof")
3218 stats = hotshot.stats.load("hg.prof")
3217 stats.strip_dirs()
3219 stats.strip_dirs()
3218 stats.sort_stats('time', 'calls')
3220 stats.sort_stats('time', 'calls')
3219 stats.print_stats(40)
3221 stats.print_stats(40)
3220 elif options['lsprof']:
3222 elif options['lsprof']:
3221 try:
3223 try:
3222 from mercurial import lsprof
3224 from mercurial import lsprof
3223 except ImportError:
3225 except ImportError:
3224 raise util.Abort(_(
3226 raise util.Abort(_(
3225 'lsprof not available - install from '
3227 'lsprof not available - install from '
3226 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3228 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3227 p = lsprof.Profiler()
3229 p = lsprof.Profiler()
3228 p.enable(subcalls=True)
3230 p.enable(subcalls=True)
3229 try:
3231 try:
3230 return d()
3232 return d()
3231 finally:
3233 finally:
3232 p.disable()
3234 p.disable()
3233 stats = lsprof.Stats(p.getstats())
3235 stats = lsprof.Stats(p.getstats())
3234 stats.sort()
3236 stats.sort()
3235 stats.pprint(top=10, file=sys.stderr, climit=5)
3237 stats.pprint(top=10, file=sys.stderr, climit=5)
3236 else:
3238 else:
3237 return d()
3239 return d()
3238 finally:
3240 finally:
3239 u.flush()
3241 u.flush()
3240 except:
3242 except:
3241 # enter the debugger when we hit an exception
3243 # enter the debugger when we hit an exception
3242 if options['debugger']:
3244 if options['debugger']:
3243 pdb.post_mortem(sys.exc_info()[2])
3245 pdb.post_mortem(sys.exc_info()[2])
3244 u.print_exc()
3246 u.print_exc()
3245 raise
3247 raise
3246 except ParseError, inst:
3248 except ParseError, inst:
3247 if inst.args[0]:
3249 if inst.args[0]:
3248 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3250 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3249 help_(u, inst.args[0])
3251 help_(u, inst.args[0])
3250 else:
3252 else:
3251 u.warn(_("hg: %s\n") % inst.args[1])
3253 u.warn(_("hg: %s\n") % inst.args[1])
3252 help_(u, 'shortlist')
3254 help_(u, 'shortlist')
3253 except AmbiguousCommand, inst:
3255 except AmbiguousCommand, inst:
3254 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3256 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3255 (inst.args[0], " ".join(inst.args[1])))
3257 (inst.args[0], " ".join(inst.args[1])))
3256 except UnknownCommand, inst:
3258 except UnknownCommand, inst:
3257 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3259 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3258 help_(u, 'shortlist')
3260 help_(u, 'shortlist')
3259 except hg.RepoError, inst:
3261 except hg.RepoError, inst:
3260 u.warn(_("abort: %s!\n") % inst)
3262 u.warn(_("abort: %s!\n") % inst)
3261 except lock.LockHeld, inst:
3263 except lock.LockHeld, inst:
3262 if inst.errno == errno.ETIMEDOUT:
3264 if inst.errno == errno.ETIMEDOUT:
3263 reason = _('timed out waiting for lock held by %s') % inst.locker
3265 reason = _('timed out waiting for lock held by %s') % inst.locker
3264 else:
3266 else:
3265 reason = _('lock held by %s') % inst.locker
3267 reason = _('lock held by %s') % inst.locker
3266 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3268 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3267 except lock.LockUnavailable, inst:
3269 except lock.LockUnavailable, inst:
3268 u.warn(_("abort: could not lock %s: %s\n") %
3270 u.warn(_("abort: could not lock %s: %s\n") %
3269 (inst.desc or inst.filename, inst.strerror))
3271 (inst.desc or inst.filename, inst.strerror))
3270 except revlog.RevlogError, inst:
3272 except revlog.RevlogError, inst:
3271 u.warn(_("abort: %s!\n") % inst)
3273 u.warn(_("abort: %s!\n") % inst)
3272 except util.SignalInterrupt:
3274 except util.SignalInterrupt:
3273 u.warn(_("killed!\n"))
3275 u.warn(_("killed!\n"))
3274 except KeyboardInterrupt:
3276 except KeyboardInterrupt:
3275 try:
3277 try:
3276 u.warn(_("interrupted!\n"))
3278 u.warn(_("interrupted!\n"))
3277 except IOError, inst:
3279 except IOError, inst:
3278 if inst.errno == errno.EPIPE:
3280 if inst.errno == errno.EPIPE:
3279 if u.debugflag:
3281 if u.debugflag:
3280 u.warn(_("\nbroken pipe\n"))
3282 u.warn(_("\nbroken pipe\n"))
3281 else:
3283 else:
3282 raise
3284 raise
3283 except socket.error, inst:
3285 except socket.error, inst:
3284 u.warn(_("abort: %s\n") % inst[1])
3286 u.warn(_("abort: %s\n") % inst[1])
3285 except IOError, inst:
3287 except IOError, inst:
3286 if hasattr(inst, "code"):
3288 if hasattr(inst, "code"):
3287 u.warn(_("abort: %s\n") % inst)
3289 u.warn(_("abort: %s\n") % inst)
3288 elif hasattr(inst, "reason"):
3290 elif hasattr(inst, "reason"):
3289 try: # usually it is in the form (errno, strerror)
3291 try: # usually it is in the form (errno, strerror)
3290 reason = inst.reason.args[1]
3292 reason = inst.reason.args[1]
3291 except: # it might be anything, for example a string
3293 except: # it might be anything, for example a string
3292 reason = inst.reason
3294 reason = inst.reason
3293 u.warn(_("abort: error: %s\n") % reason)
3295 u.warn(_("abort: error: %s\n") % reason)
3294 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3296 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3295 if u.debugflag:
3297 if u.debugflag:
3296 u.warn(_("broken pipe\n"))
3298 u.warn(_("broken pipe\n"))
3297 elif getattr(inst, "strerror", None):
3299 elif getattr(inst, "strerror", None):
3298 if getattr(inst, "filename", None):
3300 if getattr(inst, "filename", None):
3299 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3301 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3300 else:
3302 else:
3301 u.warn(_("abort: %s\n") % inst.strerror)
3303 u.warn(_("abort: %s\n") % inst.strerror)
3302 else:
3304 else:
3303 raise
3305 raise
3304 except OSError, inst:
3306 except OSError, inst:
3305 if getattr(inst, "filename", None):
3307 if getattr(inst, "filename", None):
3306 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3308 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3307 else:
3309 else:
3308 u.warn(_("abort: %s\n") % inst.strerror)
3310 u.warn(_("abort: %s\n") % inst.strerror)
3309 except util.UnexpectedOutput, inst:
3311 except util.UnexpectedOutput, inst:
3310 u.warn(_("abort: %s") % inst[0])
3312 u.warn(_("abort: %s") % inst[0])
3311 if not isinstance(inst[1], basestring):
3313 if not isinstance(inst[1], basestring):
3312 u.warn(" %r\n" % (inst[1],))
3314 u.warn(" %r\n" % (inst[1],))
3313 elif not inst[1]:
3315 elif not inst[1]:
3314 u.warn(_(" empty string\n"))
3316 u.warn(_(" empty string\n"))
3315 else:
3317 else:
3316 u.warn("\n%r\n" % util.ellipsis(inst[1]))
3318 u.warn("\n%r\n" % util.ellipsis(inst[1]))
3317 except util.Abort, inst:
3319 except util.Abort, inst:
3318 u.warn(_("abort: %s\n") % inst)
3320 u.warn(_("abort: %s\n") % inst)
3319 except TypeError, inst:
3321 except TypeError, inst:
3320 # was this an argument error?
3322 # was this an argument error?
3321 tb = traceback.extract_tb(sys.exc_info()[2])
3323 tb = traceback.extract_tb(sys.exc_info()[2])
3322 if len(tb) > 2: # no
3324 if len(tb) > 2: # no
3323 raise
3325 raise
3324 u.debug(inst, "\n")
3326 u.debug(inst, "\n")
3325 u.warn(_("%s: invalid arguments\n") % cmd)
3327 u.warn(_("%s: invalid arguments\n") % cmd)
3326 help_(u, cmd)
3328 help_(u, cmd)
3327 except SystemExit, inst:
3329 except SystemExit, inst:
3328 # Commands shouldn't sys.exit directly, but give a return code.
3330 # Commands shouldn't sys.exit directly, but give a return code.
3329 # Just in case catch this and and pass exit code to caller.
3331 # Just in case catch this and and pass exit code to caller.
3330 return inst.code
3332 return inst.code
3331 except:
3333 except:
3332 u.warn(_("** unknown exception encountered, details follow\n"))
3334 u.warn(_("** unknown exception encountered, details follow\n"))
3333 u.warn(_("** report bug details to "
3335 u.warn(_("** report bug details to "
3334 "http://www.selenic.com/mercurial/bts\n"))
3336 "http://www.selenic.com/mercurial/bts\n"))
3335 u.warn(_("** or mercurial@selenic.com\n"))
3337 u.warn(_("** or mercurial@selenic.com\n"))
3336 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3338 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3337 % version.get_version())
3339 % version.get_version())
3338 raise
3340 raise
3339
3341
3340 return -1
3342 return -1
@@ -1,554 +1,554 b''
1 """
1 """
2 dirstate.py - working directory tracking for mercurial
2 dirstate.py - working directory tracking for mercurial
3
3
4 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5
5
6 This software may be used and distributed according to the terms
6 This software may be used and distributed according to the terms
7 of the GNU General Public License, incorporated herein by reference.
7 of the GNU General Public License, incorporated herein by reference.
8 """
8 """
9
9
10 from node import *
10 from node import *
11 from i18n import _
11 from i18n import _
12 import struct, os, time, bisect, stat, strutil, util, re, errno
12 import struct, os, time, bisect, stat, strutil, util, re, errno
13
13
14 class dirstate(object):
14 class dirstate(object):
15 format = ">cllll"
15 format = ">cllll"
16
16
17 def __init__(self, opener, ui, root):
17 def __init__(self, opener, ui, root):
18 self.opener = opener
18 self.opener = opener
19 self.root = root
19 self.root = root
20 self.dirty = 0
20 self.dirty = 0
21 self.ui = ui
21 self.ui = ui
22 self.map = None
22 self.map = None
23 self.pl = None
23 self.pl = None
24 self.dirs = None
24 self.dirs = None
25 self.copymap = {}
25 self.copymap = {}
26 self.ignorefunc = None
26 self.ignorefunc = None
27 self._branch = None
27 self._branch = None
28
28
29 def wjoin(self, f):
29 def wjoin(self, f):
30 return os.path.join(self.root, f)
30 return os.path.join(self.root, f)
31
31
32 def getcwd(self):
32 def getcwd(self):
33 cwd = os.getcwd()
33 cwd = os.getcwd()
34 if cwd == self.root: return ''
34 if cwd == self.root: return ''
35 # self.root ends with a path separator if self.root is '/' or 'C:\'
35 # self.root ends with a path separator if self.root is '/' or 'C:\'
36 rootsep = self.root
36 rootsep = self.root
37 if not rootsep.endswith(os.sep):
37 if not rootsep.endswith(os.sep):
38 rootsep += os.sep
38 rootsep += os.sep
39 if cwd.startswith(rootsep):
39 if cwd.startswith(rootsep):
40 return cwd[len(rootsep):]
40 return cwd[len(rootsep):]
41 else:
41 else:
42 # we're outside the repo. return an absolute path.
42 # we're outside the repo. return an absolute path.
43 return cwd
43 return cwd
44
44
45 def hgignore(self):
45 def hgignore(self):
46 '''return the contents of .hgignore files as a list of patterns.
46 '''return the contents of .hgignore files as a list of patterns.
47
47
48 the files parsed for patterns include:
48 the files parsed for patterns include:
49 .hgignore in the repository root
49 .hgignore in the repository root
50 any additional files specified in the [ui] section of ~/.hgrc
50 any additional files specified in the [ui] section of ~/.hgrc
51
51
52 trailing white space is dropped.
52 trailing white space is dropped.
53 the escape character is backslash.
53 the escape character is backslash.
54 comments start with #.
54 comments start with #.
55 empty lines are skipped.
55 empty lines are skipped.
56
56
57 lines can be of the following formats:
57 lines can be of the following formats:
58
58
59 syntax: regexp # defaults following lines to non-rooted regexps
59 syntax: regexp # defaults following lines to non-rooted regexps
60 syntax: glob # defaults following lines to non-rooted globs
60 syntax: glob # defaults following lines to non-rooted globs
61 re:pattern # non-rooted regular expression
61 re:pattern # non-rooted regular expression
62 glob:pattern # non-rooted glob
62 glob:pattern # non-rooted glob
63 pattern # pattern of the current default type'''
63 pattern # pattern of the current default type'''
64 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
64 syntaxes = {'re': 'relre:', 'regexp': 'relre:', 'glob': 'relglob:'}
65 def parselines(fp):
65 def parselines(fp):
66 for line in fp:
66 for line in fp:
67 escape = False
67 escape = False
68 for i in xrange(len(line)):
68 for i in xrange(len(line)):
69 if escape: escape = False
69 if escape: escape = False
70 elif line[i] == '\\': escape = True
70 elif line[i] == '\\': escape = True
71 elif line[i] == '#': break
71 elif line[i] == '#': break
72 line = line[:i].rstrip()
72 line = line[:i].rstrip()
73 if line: yield line
73 if line: yield line
74 repoignore = self.wjoin('.hgignore')
74 repoignore = self.wjoin('.hgignore')
75 files = [repoignore]
75 files = [repoignore]
76 files.extend(self.ui.hgignorefiles())
76 files.extend(self.ui.hgignorefiles())
77 pats = {}
77 pats = {}
78 for f in files:
78 for f in files:
79 try:
79 try:
80 pats[f] = []
80 pats[f] = []
81 fp = open(f)
81 fp = open(f)
82 syntax = 'relre:'
82 syntax = 'relre:'
83 for line in parselines(fp):
83 for line in parselines(fp):
84 if line.startswith('syntax:'):
84 if line.startswith('syntax:'):
85 s = line[7:].strip()
85 s = line[7:].strip()
86 try:
86 try:
87 syntax = syntaxes[s]
87 syntax = syntaxes[s]
88 except KeyError:
88 except KeyError:
89 self.ui.warn(_("%s: ignoring invalid "
89 self.ui.warn(_("%s: ignoring invalid "
90 "syntax '%s'\n") % (f, s))
90 "syntax '%s'\n") % (f, s))
91 continue
91 continue
92 pat = syntax + line
92 pat = syntax + line
93 for s in syntaxes.values():
93 for s in syntaxes.values():
94 if line.startswith(s):
94 if line.startswith(s):
95 pat = line
95 pat = line
96 break
96 break
97 pats[f].append(pat)
97 pats[f].append(pat)
98 except IOError, inst:
98 except IOError, inst:
99 if f != repoignore:
99 if f != repoignore:
100 self.ui.warn(_("skipping unreadable ignore file"
100 self.ui.warn(_("skipping unreadable ignore file"
101 " '%s': %s\n") % (f, inst.strerror))
101 " '%s': %s\n") % (f, inst.strerror))
102 return pats
102 return pats
103
103
104 def ignore(self, fn):
104 def ignore(self, fn):
105 '''default match function used by dirstate and
105 '''default match function used by dirstate and
106 localrepository. this honours the repository .hgignore file
106 localrepository. this honours the repository .hgignore file
107 and any other files specified in the [ui] section of .hgrc.'''
107 and any other files specified in the [ui] section of .hgrc.'''
108 if not self.ignorefunc:
108 if not self.ignorefunc:
109 ignore = self.hgignore()
109 ignore = self.hgignore()
110 allpats = []
110 allpats = []
111 [allpats.extend(patlist) for patlist in ignore.values()]
111 [allpats.extend(patlist) for patlist in ignore.values()]
112 if allpats:
112 if allpats:
113 try:
113 try:
114 files, self.ignorefunc, anypats = (
114 files, self.ignorefunc, anypats = (
115 util.matcher(self.root, inc=allpats, src='.hgignore'))
115 util.matcher(self.root, inc=allpats, src='.hgignore'))
116 except util.Abort:
116 except util.Abort:
117 # Re-raise an exception where the src is the right file
117 # Re-raise an exception where the src is the right file
118 for f, patlist in ignore.items():
118 for f, patlist in ignore.items():
119 files, self.ignorefunc, anypats = (
119 files, self.ignorefunc, anypats = (
120 util.matcher(self.root, inc=patlist, src=f))
120 util.matcher(self.root, inc=patlist, src=f))
121 else:
121 else:
122 self.ignorefunc = util.never
122 self.ignorefunc = util.never
123 return self.ignorefunc(fn)
123 return self.ignorefunc(fn)
124
124
125 def __del__(self):
125 def __del__(self):
126 if self.dirty:
126 if self.dirty:
127 self.write()
127 self.write()
128
128
129 def __getitem__(self, key):
129 def __getitem__(self, key):
130 try:
130 try:
131 return self.map[key]
131 return self.map[key]
132 except TypeError:
132 except TypeError:
133 self.lazyread()
133 self.lazyread()
134 return self[key]
134 return self[key]
135
135
136 def __contains__(self, key):
136 def __contains__(self, key):
137 self.lazyread()
137 self.lazyread()
138 return key in self.map
138 return key in self.map
139
139
140 def parents(self):
140 def parents(self):
141 self.lazyread()
141 self.lazyread()
142 return self.pl
142 return self.pl
143
143
144 def branch(self):
144 def branch(self):
145 if not self._branch:
145 if not self._branch:
146 try:
146 try:
147 self._branch = self.opener("branch").read().strip()\
147 self._branch = self.opener("branch").read().strip()\
148 or "default"
148 or "default"
149 except IOError:
149 except IOError:
150 self._branch = "default"
150 self._branch = "default"
151 return self._branch
151 return self._branch
152
152
153 def markdirty(self):
153 def markdirty(self):
154 if not self.dirty:
154 if not self.dirty:
155 self.dirty = 1
155 self.dirty = 1
156
156
157 def setparents(self, p1, p2=nullid):
157 def setparents(self, p1, p2=nullid):
158 self.lazyread()
158 self.lazyread()
159 self.markdirty()
159 self.markdirty()
160 self.pl = p1, p2
160 self.pl = p1, p2
161
161
162 def setbranch(self, branch):
162 def setbranch(self, branch):
163 self._branch = branch
163 self._branch = branch
164 self.opener("branch", "w").write(branch + '\n')
164 self.opener("branch", "w").write(branch + '\n')
165
165
166 def state(self, key):
166 def state(self, key):
167 try:
167 try:
168 return self[key][0]
168 return self[key][0]
169 except KeyError:
169 except KeyError:
170 return "?"
170 return "?"
171
171
172 def lazyread(self):
172 def lazyread(self):
173 if self.map is None:
173 if self.map is None:
174 self.read()
174 self.read()
175
175
176 def parse(self, st):
176 def parse(self, st):
177 self.pl = [st[:20], st[20: 40]]
177 self.pl = [st[:20], st[20: 40]]
178
178
179 # deref fields so they will be local in loop
179 # deref fields so they will be local in loop
180 map = self.map
180 map = self.map
181 copymap = self.copymap
181 copymap = self.copymap
182 format = self.format
182 format = self.format
183 unpack = struct.unpack
183 unpack = struct.unpack
184
184
185 pos = 40
185 pos = 40
186 e_size = struct.calcsize(format)
186 e_size = struct.calcsize(format)
187
187
188 while pos < len(st):
188 while pos < len(st):
189 newpos = pos + e_size
189 newpos = pos + e_size
190 e = unpack(format, st[pos:newpos])
190 e = unpack(format, st[pos:newpos])
191 l = e[4]
191 l = e[4]
192 pos = newpos
192 pos = newpos
193 newpos = pos + l
193 newpos = pos + l
194 f = st[pos:newpos]
194 f = st[pos:newpos]
195 if '\0' in f:
195 if '\0' in f:
196 f, c = f.split('\0')
196 f, c = f.split('\0')
197 copymap[f] = c
197 copymap[f] = c
198 map[f] = e[:4]
198 map[f] = e[:4]
199 pos = newpos
199 pos = newpos
200
200
201 def read(self):
201 def read(self):
202 self.map = {}
202 self.map = {}
203 self.pl = [nullid, nullid]
203 self.pl = [nullid, nullid]
204 try:
204 try:
205 st = self.opener("dirstate").read()
205 st = self.opener("dirstate").read()
206 if st:
206 if st:
207 self.parse(st)
207 self.parse(st)
208 except IOError, err:
208 except IOError, err:
209 if err.errno != errno.ENOENT: raise
209 if err.errno != errno.ENOENT: raise
210
210
211 def copy(self, source, dest):
211 def copy(self, source, dest):
212 self.lazyread()
212 self.lazyread()
213 self.markdirty()
213 self.markdirty()
214 self.copymap[dest] = source
214 self.copymap[dest] = source
215
215
216 def copied(self, file):
216 def copied(self, file):
217 return self.copymap.get(file, None)
217 return self.copymap.get(file, None)
218
218
219 def copies(self):
219 def copies(self):
220 return self.copymap
220 return self.copymap
221
221
222 def initdirs(self):
222 def initdirs(self):
223 if self.dirs is None:
223 if self.dirs is None:
224 self.dirs = {}
224 self.dirs = {}
225 for f in self.map:
225 for f in self.map:
226 self.updatedirs(f, 1)
226 self.updatedirs(f, 1)
227
227
228 def updatedirs(self, path, delta):
228 def updatedirs(self, path, delta):
229 if self.dirs is not None:
229 if self.dirs is not None:
230 for c in strutil.findall(path, '/'):
230 for c in strutil.findall(path, '/'):
231 pc = path[:c]
231 pc = path[:c]
232 self.dirs.setdefault(pc, 0)
232 self.dirs.setdefault(pc, 0)
233 self.dirs[pc] += delta
233 self.dirs[pc] += delta
234
234
235 def checkinterfering(self, files):
235 def checkinterfering(self, files):
236 def prefixes(f):
236 def prefixes(f):
237 for c in strutil.rfindall(f, '/'):
237 for c in strutil.rfindall(f, '/'):
238 yield f[:c]
238 yield f[:c]
239 self.lazyread()
239 self.lazyread()
240 self.initdirs()
240 self.initdirs()
241 seendirs = {}
241 seendirs = {}
242 for f in files:
242 for f in files:
243 # shadows
243 # shadows
244 if self.dirs.get(f):
244 if self.dirs.get(f):
245 raise util.Abort(_('directory named %r already in dirstate') %
245 raise util.Abort(_('directory named %r already in dirstate') %
246 f)
246 f)
247 for d in prefixes(f):
247 for d in prefixes(f):
248 if d in seendirs:
248 if d in seendirs:
249 break
249 break
250 if d in self.map:
250 if d in self.map:
251 raise util.Abort(_('file named %r already in dirstate') %
251 raise util.Abort(_('file named %r already in dirstate') %
252 d)
252 d)
253 seendirs[d] = True
253 seendirs[d] = True
254 # disallowed
254 # disallowed
255 if '\r' in f or '\n' in f:
255 if '\r' in f or '\n' in f:
256 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames"))
256 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames"))
257
257
258 def update(self, files, state, **kw):
258 def update(self, files, state, **kw):
259 ''' current states:
259 ''' current states:
260 n normal
260 n normal
261 m needs merging
261 m needs merging
262 r marked for removal
262 r marked for removal
263 a marked for addition'''
263 a marked for addition'''
264
264
265 if not files: return
265 if not files: return
266 self.lazyread()
266 self.lazyread()
267 self.markdirty()
267 self.markdirty()
268 if state == "a":
268 if state == "a":
269 self.initdirs()
269 self.initdirs()
270 self.checkinterfering(files)
270 self.checkinterfering(files)
271 for f in files:
271 for f in files:
272 if state == "r":
272 if state == "r":
273 self.map[f] = ('r', 0, 0, 0)
273 self.map[f] = ('r', 0, 0, 0)
274 self.updatedirs(f, -1)
274 self.updatedirs(f, -1)
275 else:
275 else:
276 if state == "a":
276 if state == "a":
277 self.updatedirs(f, 1)
277 self.updatedirs(f, 1)
278 s = os.lstat(self.wjoin(f))
278 s = os.lstat(self.wjoin(f))
279 st_size = kw.get('st_size', s.st_size)
279 st_size = kw.get('st_size', s.st_size)
280 st_mtime = kw.get('st_mtime', s.st_mtime)
280 st_mtime = kw.get('st_mtime', s.st_mtime)
281 self.map[f] = (state, s.st_mode, st_size, st_mtime)
281 self.map[f] = (state, s.st_mode, st_size, st_mtime)
282 if self.copymap.has_key(f):
282 if self.copymap.has_key(f):
283 del self.copymap[f]
283 del self.copymap[f]
284
284
285 def forget(self, files):
285 def forget(self, files):
286 if not files: return
286 if not files: return
287 self.lazyread()
287 self.lazyread()
288 self.markdirty()
288 self.markdirty()
289 self.initdirs()
289 self.initdirs()
290 for f in files:
290 for f in files:
291 try:
291 try:
292 del self.map[f]
292 del self.map[f]
293 self.updatedirs(f, -1)
293 self.updatedirs(f, -1)
294 except KeyError:
294 except KeyError:
295 self.ui.warn(_("not in dirstate: %s!\n") % f)
295 self.ui.warn(_("not in dirstate: %s!\n") % f)
296 pass
296 pass
297
297
298 def clear(self):
298 def clear(self):
299 self.map = {}
299 self.map = {}
300 self.copymap = {}
300 self.copymap = {}
301 self.dirs = None
301 self.dirs = None
302 self.markdirty()
302 self.markdirty()
303
303
304 def rebuild(self, parent, files):
304 def rebuild(self, parent, files):
305 self.clear()
305 self.clear()
306 for f in files:
306 for f in files:
307 if files.execf(f):
307 if files.execf(f):
308 self.map[f] = ('n', 0777, -1, 0)
308 self.map[f] = ('n', 0777, -1, 0)
309 else:
309 else:
310 self.map[f] = ('n', 0666, -1, 0)
310 self.map[f] = ('n', 0666, -1, 0)
311 self.pl = (parent, nullid)
311 self.pl = (parent, nullid)
312 self.markdirty()
312 self.markdirty()
313
313
314 def write(self):
314 def write(self):
315 if not self.dirty:
315 if not self.dirty:
316 return
316 return
317 st = self.opener("dirstate", "w", atomic=True)
317 st = self.opener("dirstate", "w", atomic=True)
318 st.write("".join(self.pl))
318 st.write("".join(self.pl))
319 for f, e in self.map.items():
319 for f, e in self.map.items():
320 c = self.copied(f)
320 c = self.copied(f)
321 if c:
321 if c:
322 f = f + "\0" + c
322 f = f + "\0" + c
323 e = struct.pack(self.format, e[0], e[1], e[2], e[3], len(f))
323 e = struct.pack(self.format, e[0], e[1], e[2], e[3], len(f))
324 st.write(e + f)
324 st.write(e + f)
325 self.dirty = 0
325 self.dirty = 0
326
326
327 def filterfiles(self, files):
327 def filterfiles(self, files):
328 ret = {}
328 ret = {}
329 unknown = []
329 unknown = []
330
330
331 for x in files:
331 for x in files:
332 if x == '.':
332 if x == '.':
333 return self.map.copy()
333 return self.map.copy()
334 if x not in self.map:
334 if x not in self.map:
335 unknown.append(x)
335 unknown.append(x)
336 else:
336 else:
337 ret[x] = self.map[x]
337 ret[x] = self.map[x]
338
338
339 if not unknown:
339 if not unknown:
340 return ret
340 return ret
341
341
342 b = self.map.keys()
342 b = self.map.keys()
343 b.sort()
343 b.sort()
344 blen = len(b)
344 blen = len(b)
345
345
346 for x in unknown:
346 for x in unknown:
347 bs = bisect.bisect(b, "%s%s" % (x, '/'))
347 bs = bisect.bisect(b, "%s%s" % (x, '/'))
348 while bs < blen:
348 while bs < blen:
349 s = b[bs]
349 s = b[bs]
350 if len(s) > len(x) and s.startswith(x):
350 if len(s) > len(x) and s.startswith(x):
351 ret[s] = self.map[s]
351 ret[s] = self.map[s]
352 else:
352 else:
353 break
353 break
354 bs += 1
354 bs += 1
355 return ret
355 return ret
356
356
357 def supported_type(self, f, st, verbose=False):
357 def supported_type(self, f, st, verbose=False):
358 if stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode):
358 if stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode):
359 return True
359 return True
360 if verbose:
360 if verbose:
361 kind = 'unknown'
361 kind = 'unknown'
362 if stat.S_ISCHR(st.st_mode): kind = _('character device')
362 if stat.S_ISCHR(st.st_mode): kind = _('character device')
363 elif stat.S_ISBLK(st.st_mode): kind = _('block device')
363 elif stat.S_ISBLK(st.st_mode): kind = _('block device')
364 elif stat.S_ISFIFO(st.st_mode): kind = _('fifo')
364 elif stat.S_ISFIFO(st.st_mode): kind = _('fifo')
365 elif stat.S_ISSOCK(st.st_mode): kind = _('socket')
365 elif stat.S_ISSOCK(st.st_mode): kind = _('socket')
366 elif stat.S_ISDIR(st.st_mode): kind = _('directory')
366 elif stat.S_ISDIR(st.st_mode): kind = _('directory')
367 self.ui.warn(_('%s: unsupported file type (type is %s)\n') % (
367 self.ui.warn(_('%s: unsupported file type (type is %s)\n') % (
368 util.pathto(self.root, self.getcwd(), f),
368 util.pathto(self.root, self.getcwd(), f),
369 kind))
369 kind))
370 return False
370 return False
371
371
372 def walk(self, files=None, match=util.always, badmatch=None):
372 def walk(self, files=None, match=util.always, badmatch=None):
373 # filter out the stat
373 # filter out the stat
374 for src, f, st in self.statwalk(files, match, badmatch=badmatch):
374 for src, f, st in self.statwalk(files, match, badmatch=badmatch):
375 yield src, f
375 yield src, f
376
376
377 def statwalk(self, files=None, match=util.always, ignored=False,
377 def statwalk(self, files=None, match=util.always, ignored=False,
378 badmatch=None, directories=False):
378 badmatch=None, directories=False):
379 '''
379 '''
380 walk recursively through the directory tree, finding all files
380 walk recursively through the directory tree, finding all files
381 matched by the match function
381 matched by the match function
382
382
383 results are yielded in a tuple (src, filename, st), where src
383 results are yielded in a tuple (src, filename, st), where src
384 is one of:
384 is one of:
385 'f' the file was found in the directory tree
385 'f' the file was found in the directory tree
386 'd' the file is a directory of the tree
386 'd' the file is a directory of the tree
387 'm' the file was only in the dirstate and not in the tree
387 'm' the file was only in the dirstate and not in the tree
388 'b' file was not found and matched badmatch
388 'b' file was not found and matched badmatch
389
389
390 and st is the stat result if the file was found in the directory.
390 and st is the stat result if the file was found in the directory.
391 '''
391 '''
392 self.lazyread()
392 self.lazyread()
393
393
394 # walk all files by default
394 # walk all files by default
395 if not files:
395 if not files:
396 files = ['.']
396 files = ['.']
397 dc = self.map.copy()
397 dc = self.map.copy()
398 else:
398 else:
399 files = util.unique(files)
399 files = util.unique(files)
400 dc = self.filterfiles(files)
400 dc = self.filterfiles(files)
401
401
402 def imatch(file_):
402 def imatch(file_):
403 if file_ not in dc and self.ignore(file_):
403 if file_ not in dc and self.ignore(file_):
404 return False
404 return False
405 return match(file_)
405 return match(file_)
406
406
407 ignore = self.ignore
407 ignore = self.ignore
408 if ignored:
408 if ignored:
409 imatch = match
409 imatch = match
410 ignore = util.never
410 ignore = util.never
411
411
412 # self.root may end with a path separator when self.root == '/'
412 # self.root may end with a path separator when self.root == '/'
413 common_prefix_len = len(self.root)
413 common_prefix_len = len(self.root)
414 if not self.root.endswith(os.sep):
414 if not self.root.endswith(os.sep):
415 common_prefix_len += 1
415 common_prefix_len += 1
416 # recursion free walker, faster than os.walk.
416 # recursion free walker, faster than os.walk.
417 def findfiles(s):
417 def findfiles(s):
418 work = [s]
418 work = [s]
419 if directories:
419 if directories:
420 yield 'd', util.normpath(s[common_prefix_len:]), os.lstat(s)
420 yield 'd', util.normpath(s[common_prefix_len:]), os.lstat(s)
421 while work:
421 while work:
422 top = work.pop()
422 top = work.pop()
423 names = os.listdir(top)
423 names = os.listdir(top)
424 names.sort()
424 names.sort()
425 # nd is the top of the repository dir tree
425 # nd is the top of the repository dir tree
426 nd = util.normpath(top[common_prefix_len:])
426 nd = util.normpath(top[common_prefix_len:])
427 if nd == '.':
427 if nd == '.':
428 nd = ''
428 nd = ''
429 else:
429 else:
430 # do not recurse into a repo contained in this
430 # do not recurse into a repo contained in this
431 # one. use bisect to find .hg directory so speed
431 # one. use bisect to find .hg directory so speed
432 # is good on big directory.
432 # is good on big directory.
433 hg = bisect.bisect_left(names, '.hg')
433 hg = bisect.bisect_left(names, '.hg')
434 if hg < len(names) and names[hg] == '.hg':
434 if hg < len(names) and names[hg] == '.hg':
435 if os.path.isdir(os.path.join(top, '.hg')):
435 if os.path.isdir(os.path.join(top, '.hg')):
436 continue
436 continue
437 for f in names:
437 for f in names:
438 np = util.pconvert(os.path.join(nd, f))
438 np = util.pconvert(os.path.join(nd, f))
439 if seen(np):
439 if seen(np):
440 continue
440 continue
441 p = os.path.join(top, f)
441 p = os.path.join(top, f)
442 # don't trip over symlinks
442 # don't trip over symlinks
443 st = os.lstat(p)
443 st = os.lstat(p)
444 if stat.S_ISDIR(st.st_mode):
444 if stat.S_ISDIR(st.st_mode):
445 if not ignore(p):
445 if not ignore(np):
446 work.append(p)
446 work.append(p)
447 if directories:
447 if directories:
448 yield 'd', np, st
448 yield 'd', np, st
449 if imatch(np) and np in dc:
449 if imatch(np) and np in dc:
450 yield 'm', np, st
450 yield 'm', np, st
451 elif imatch(np):
451 elif imatch(np):
452 if self.supported_type(np, st):
452 if self.supported_type(np, st):
453 yield 'f', np, st
453 yield 'f', np, st
454 elif np in dc:
454 elif np in dc:
455 yield 'm', np, st
455 yield 'm', np, st
456
456
457 known = {'.hg': 1}
457 known = {'.hg': 1}
458 def seen(fn):
458 def seen(fn):
459 if fn in known: return True
459 if fn in known: return True
460 known[fn] = 1
460 known[fn] = 1
461
461
462 # step one, find all files that match our criteria
462 # step one, find all files that match our criteria
463 files.sort()
463 files.sort()
464 for ff in files:
464 for ff in files:
465 nf = util.normpath(ff)
465 nf = util.normpath(ff)
466 f = self.wjoin(ff)
466 f = self.wjoin(ff)
467 try:
467 try:
468 st = os.lstat(f)
468 st = os.lstat(f)
469 except OSError, inst:
469 except OSError, inst:
470 found = False
470 found = False
471 for fn in dc:
471 for fn in dc:
472 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
472 if nf == fn or (fn.startswith(nf) and fn[len(nf)] == '/'):
473 found = True
473 found = True
474 break
474 break
475 if not found:
475 if not found:
476 if inst.errno != errno.ENOENT or not badmatch:
476 if inst.errno != errno.ENOENT or not badmatch:
477 self.ui.warn('%s: %s\n' % (
477 self.ui.warn('%s: %s\n' % (
478 util.pathto(self.root, self.getcwd(), ff),
478 util.pathto(self.root, self.getcwd(), ff),
479 inst.strerror))
479 inst.strerror))
480 elif badmatch and badmatch(ff) and imatch(nf):
480 elif badmatch and badmatch(ff) and imatch(nf):
481 yield 'b', ff, None
481 yield 'b', ff, None
482 continue
482 continue
483 if stat.S_ISDIR(st.st_mode):
483 if stat.S_ISDIR(st.st_mode):
484 cmp1 = (lambda x, y: cmp(x[1], y[1]))
484 cmp1 = (lambda x, y: cmp(x[1], y[1]))
485 sorted_ = [ x for x in findfiles(f) ]
485 sorted_ = [ x for x in findfiles(f) ]
486 sorted_.sort(cmp1)
486 sorted_.sort(cmp1)
487 for e in sorted_:
487 for e in sorted_:
488 yield e
488 yield e
489 else:
489 else:
490 if not seen(nf) and match(nf):
490 if not seen(nf) and match(nf):
491 if self.supported_type(ff, st, verbose=True):
491 if self.supported_type(ff, st, verbose=True):
492 yield 'f', nf, st
492 yield 'f', nf, st
493 elif ff in dc:
493 elif ff in dc:
494 yield 'm', nf, st
494 yield 'm', nf, st
495
495
496 # step two run through anything left in the dc hash and yield
496 # step two run through anything left in the dc hash and yield
497 # if we haven't already seen it
497 # if we haven't already seen it
498 ks = dc.keys()
498 ks = dc.keys()
499 ks.sort()
499 ks.sort()
500 for k in ks:
500 for k in ks:
501 if not seen(k) and imatch(k):
501 if not seen(k) and imatch(k):
502 yield 'm', k, None
502 yield 'm', k, None
503
503
504 def status(self, files=None, match=util.always, list_ignored=False,
504 def status(self, files=None, match=util.always, list_ignored=False,
505 list_clean=False):
505 list_clean=False):
506 lookup, modified, added, unknown, ignored = [], [], [], [], []
506 lookup, modified, added, unknown, ignored = [], [], [], [], []
507 removed, deleted, clean = [], [], []
507 removed, deleted, clean = [], [], []
508
508
509 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
509 for src, fn, st in self.statwalk(files, match, ignored=list_ignored):
510 try:
510 try:
511 type_, mode, size, time = self[fn]
511 type_, mode, size, time = self[fn]
512 except KeyError:
512 except KeyError:
513 if list_ignored and self.ignore(fn):
513 if list_ignored and self.ignore(fn):
514 ignored.append(fn)
514 ignored.append(fn)
515 else:
515 else:
516 unknown.append(fn)
516 unknown.append(fn)
517 continue
517 continue
518 if src == 'm':
518 if src == 'm':
519 nonexistent = True
519 nonexistent = True
520 if not st:
520 if not st:
521 try:
521 try:
522 st = os.lstat(self.wjoin(fn))
522 st = os.lstat(self.wjoin(fn))
523 except OSError, inst:
523 except OSError, inst:
524 if inst.errno != errno.ENOENT:
524 if inst.errno != errno.ENOENT:
525 raise
525 raise
526 st = None
526 st = None
527 # We need to re-check that it is a valid file
527 # We need to re-check that it is a valid file
528 if st and self.supported_type(fn, st):
528 if st and self.supported_type(fn, st):
529 nonexistent = False
529 nonexistent = False
530 # XXX: what to do with file no longer present in the fs
530 # XXX: what to do with file no longer present in the fs
531 # who are not removed in the dirstate ?
531 # who are not removed in the dirstate ?
532 if nonexistent and type_ in "nm":
532 if nonexistent and type_ in "nm":
533 deleted.append(fn)
533 deleted.append(fn)
534 continue
534 continue
535 # check the common case first
535 # check the common case first
536 if type_ == 'n':
536 if type_ == 'n':
537 if not st:
537 if not st:
538 st = os.lstat(self.wjoin(fn))
538 st = os.lstat(self.wjoin(fn))
539 if size >= 0 and (size != st.st_size
539 if size >= 0 and (size != st.st_size
540 or (mode ^ st.st_mode) & 0100):
540 or (mode ^ st.st_mode) & 0100):
541 modified.append(fn)
541 modified.append(fn)
542 elif time != int(st.st_mtime):
542 elif time != int(st.st_mtime):
543 lookup.append(fn)
543 lookup.append(fn)
544 elif list_clean:
544 elif list_clean:
545 clean.append(fn)
545 clean.append(fn)
546 elif type_ == 'm':
546 elif type_ == 'm':
547 modified.append(fn)
547 modified.append(fn)
548 elif type_ == 'a':
548 elif type_ == 'a':
549 added.append(fn)
549 added.append(fn)
550 elif type_ == 'r':
550 elif type_ == 'r':
551 removed.append(fn)
551 removed.append(fn)
552
552
553 return (lookup, modified, added, removed, deleted, unknown, ignored,
553 return (lookup, modified, added, removed, deleted, unknown, ignored,
554 clean)
554 clean)
@@ -1,1472 +1,1472 b''
1 """
1 """
2 util.py - Mercurial utility functions and platform specfic implementations
2 util.py - Mercurial utility functions and platform specfic implementations
3
3
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 Copyright 2005 K. Thananchayan <thananck@yahoo.com>
5 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
7
7
8 This software may be used and distributed according to the terms
8 This software may be used and distributed according to the terms
9 of the GNU General Public License, incorporated herein by reference.
9 of the GNU General Public License, incorporated herein by reference.
10
10
11 This contains helper routines that are independent of the SCM core and hide
11 This contains helper routines that are independent of the SCM core and hide
12 platform-specific details from the core.
12 platform-specific details from the core.
13 """
13 """
14
14
15 from i18n import _
15 from i18n import _
16 import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile
16 import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile
17 import os, threading, time, calendar, ConfigParser, locale, glob
17 import os, threading, time, calendar, ConfigParser, locale, glob
18
18
19 try:
19 try:
20 _encoding = os.environ.get("HGENCODING") or locale.getpreferredencoding() \
20 _encoding = os.environ.get("HGENCODING") or locale.getpreferredencoding() \
21 or "ascii"
21 or "ascii"
22 except locale.Error:
22 except locale.Error:
23 _encoding = 'ascii'
23 _encoding = 'ascii'
24 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
24 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
25 _fallbackencoding = 'ISO-8859-1'
25 _fallbackencoding = 'ISO-8859-1'
26
26
27 def tolocal(s):
27 def tolocal(s):
28 """
28 """
29 Convert a string from internal UTF-8 to local encoding
29 Convert a string from internal UTF-8 to local encoding
30
30
31 All internal strings should be UTF-8 but some repos before the
31 All internal strings should be UTF-8 but some repos before the
32 implementation of locale support may contain latin1 or possibly
32 implementation of locale support may contain latin1 or possibly
33 other character sets. We attempt to decode everything strictly
33 other character sets. We attempt to decode everything strictly
34 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
34 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
35 replace unknown characters.
35 replace unknown characters.
36 """
36 """
37 for e in ('UTF-8', _fallbackencoding):
37 for e in ('UTF-8', _fallbackencoding):
38 try:
38 try:
39 u = s.decode(e) # attempt strict decoding
39 u = s.decode(e) # attempt strict decoding
40 return u.encode(_encoding, "replace")
40 return u.encode(_encoding, "replace")
41 except LookupError, k:
41 except LookupError, k:
42 raise Abort(_("%s, please check your locale settings") % k)
42 raise Abort(_("%s, please check your locale settings") % k)
43 except UnicodeDecodeError:
43 except UnicodeDecodeError:
44 pass
44 pass
45 u = s.decode("utf-8", "replace") # last ditch
45 u = s.decode("utf-8", "replace") # last ditch
46 return u.encode(_encoding, "replace")
46 return u.encode(_encoding, "replace")
47
47
48 def fromlocal(s):
48 def fromlocal(s):
49 """
49 """
50 Convert a string from the local character encoding to UTF-8
50 Convert a string from the local character encoding to UTF-8
51
51
52 We attempt to decode strings using the encoding mode set by
52 We attempt to decode strings using the encoding mode set by
53 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
53 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
54 characters will cause an error message. Other modes include
54 characters will cause an error message. Other modes include
55 'replace', which replaces unknown characters with a special
55 'replace', which replaces unknown characters with a special
56 Unicode character, and 'ignore', which drops the character.
56 Unicode character, and 'ignore', which drops the character.
57 """
57 """
58 try:
58 try:
59 return s.decode(_encoding, _encodingmode).encode("utf-8")
59 return s.decode(_encoding, _encodingmode).encode("utf-8")
60 except UnicodeDecodeError, inst:
60 except UnicodeDecodeError, inst:
61 sub = s[max(0, inst.start-10):inst.start+10]
61 sub = s[max(0, inst.start-10):inst.start+10]
62 raise Abort("decoding near '%s': %s!" % (sub, inst))
62 raise Abort("decoding near '%s': %s!" % (sub, inst))
63 except LookupError, k:
63 except LookupError, k:
64 raise Abort(_("%s, please check your locale settings") % k)
64 raise Abort(_("%s, please check your locale settings") % k)
65
65
66 def locallen(s):
66 def locallen(s):
67 """Find the length in characters of a local string"""
67 """Find the length in characters of a local string"""
68 return len(s.decode(_encoding, "replace"))
68 return len(s.decode(_encoding, "replace"))
69
69
70 def localsub(s, a, b=None):
70 def localsub(s, a, b=None):
71 try:
71 try:
72 u = s.decode(_encoding, _encodingmode)
72 u = s.decode(_encoding, _encodingmode)
73 if b is not None:
73 if b is not None:
74 u = u[a:b]
74 u = u[a:b]
75 else:
75 else:
76 u = u[:a]
76 u = u[:a]
77 return u.encode(_encoding, _encodingmode)
77 return u.encode(_encoding, _encodingmode)
78 except UnicodeDecodeError, inst:
78 except UnicodeDecodeError, inst:
79 sub = s[max(0, inst.start-10), inst.start+10]
79 sub = s[max(0, inst.start-10), inst.start+10]
80 raise Abort(_("decoding near '%s': %s!\n") % (sub, inst))
80 raise Abort(_("decoding near '%s': %s!\n") % (sub, inst))
81
81
82 # used by parsedate
82 # used by parsedate
83 defaultdateformats = (
83 defaultdateformats = (
84 '%Y-%m-%d %H:%M:%S',
84 '%Y-%m-%d %H:%M:%S',
85 '%Y-%m-%d %I:%M:%S%p',
85 '%Y-%m-%d %I:%M:%S%p',
86 '%Y-%m-%d %H:%M',
86 '%Y-%m-%d %H:%M',
87 '%Y-%m-%d %I:%M%p',
87 '%Y-%m-%d %I:%M%p',
88 '%Y-%m-%d',
88 '%Y-%m-%d',
89 '%m-%d',
89 '%m-%d',
90 '%m/%d',
90 '%m/%d',
91 '%m/%d/%y',
91 '%m/%d/%y',
92 '%m/%d/%Y',
92 '%m/%d/%Y',
93 '%a %b %d %H:%M:%S %Y',
93 '%a %b %d %H:%M:%S %Y',
94 '%a %b %d %I:%M:%S%p %Y',
94 '%a %b %d %I:%M:%S%p %Y',
95 '%b %d %H:%M:%S %Y',
95 '%b %d %H:%M:%S %Y',
96 '%b %d %I:%M:%S%p %Y',
96 '%b %d %I:%M:%S%p %Y',
97 '%b %d %H:%M:%S',
97 '%b %d %H:%M:%S',
98 '%b %d %I:%M:%S%p',
98 '%b %d %I:%M:%S%p',
99 '%b %d %H:%M',
99 '%b %d %H:%M',
100 '%b %d %I:%M%p',
100 '%b %d %I:%M%p',
101 '%b %d %Y',
101 '%b %d %Y',
102 '%b %d',
102 '%b %d',
103 '%H:%M:%S',
103 '%H:%M:%S',
104 '%I:%M:%SP',
104 '%I:%M:%SP',
105 '%H:%M',
105 '%H:%M',
106 '%I:%M%p',
106 '%I:%M%p',
107 )
107 )
108
108
109 extendeddateformats = defaultdateformats + (
109 extendeddateformats = defaultdateformats + (
110 "%Y",
110 "%Y",
111 "%Y-%m",
111 "%Y-%m",
112 "%b",
112 "%b",
113 "%b %Y",
113 "%b %Y",
114 )
114 )
115
115
116 class SignalInterrupt(Exception):
116 class SignalInterrupt(Exception):
117 """Exception raised on SIGTERM and SIGHUP."""
117 """Exception raised on SIGTERM and SIGHUP."""
118
118
119 # differences from SafeConfigParser:
119 # differences from SafeConfigParser:
120 # - case-sensitive keys
120 # - case-sensitive keys
121 # - allows values that are not strings (this means that you may not
121 # - allows values that are not strings (this means that you may not
122 # be able to save the configuration to a file)
122 # be able to save the configuration to a file)
123 class configparser(ConfigParser.SafeConfigParser):
123 class configparser(ConfigParser.SafeConfigParser):
124 def optionxform(self, optionstr):
124 def optionxform(self, optionstr):
125 return optionstr
125 return optionstr
126
126
127 def set(self, section, option, value):
127 def set(self, section, option, value):
128 return ConfigParser.ConfigParser.set(self, section, option, value)
128 return ConfigParser.ConfigParser.set(self, section, option, value)
129
129
130 def _interpolate(self, section, option, rawval, vars):
130 def _interpolate(self, section, option, rawval, vars):
131 if not isinstance(rawval, basestring):
131 if not isinstance(rawval, basestring):
132 return rawval
132 return rawval
133 return ConfigParser.SafeConfigParser._interpolate(self, section,
133 return ConfigParser.SafeConfigParser._interpolate(self, section,
134 option, rawval, vars)
134 option, rawval, vars)
135
135
136 def cachefunc(func):
136 def cachefunc(func):
137 '''cache the result of function calls'''
137 '''cache the result of function calls'''
138 # XXX doesn't handle keywords args
138 # XXX doesn't handle keywords args
139 cache = {}
139 cache = {}
140 if func.func_code.co_argcount == 1:
140 if func.func_code.co_argcount == 1:
141 # we gain a small amount of time because
141 # we gain a small amount of time because
142 # we don't need to pack/unpack the list
142 # we don't need to pack/unpack the list
143 def f(arg):
143 def f(arg):
144 if arg not in cache:
144 if arg not in cache:
145 cache[arg] = func(arg)
145 cache[arg] = func(arg)
146 return cache[arg]
146 return cache[arg]
147 else:
147 else:
148 def f(*args):
148 def f(*args):
149 if args not in cache:
149 if args not in cache:
150 cache[args] = func(*args)
150 cache[args] = func(*args)
151 return cache[args]
151 return cache[args]
152
152
153 return f
153 return f
154
154
155 def pipefilter(s, cmd):
155 def pipefilter(s, cmd):
156 '''filter string S through command CMD, returning its output'''
156 '''filter string S through command CMD, returning its output'''
157 (pout, pin) = popen2.popen2(cmd, -1, 'b')
157 (pout, pin) = popen2.popen2(cmd, -1, 'b')
158 def writer():
158 def writer():
159 try:
159 try:
160 pin.write(s)
160 pin.write(s)
161 pin.close()
161 pin.close()
162 except IOError, inst:
162 except IOError, inst:
163 if inst.errno != errno.EPIPE:
163 if inst.errno != errno.EPIPE:
164 raise
164 raise
165
165
166 # we should use select instead on UNIX, but this will work on most
166 # we should use select instead on UNIX, but this will work on most
167 # systems, including Windows
167 # systems, including Windows
168 w = threading.Thread(target=writer)
168 w = threading.Thread(target=writer)
169 w.start()
169 w.start()
170 f = pout.read()
170 f = pout.read()
171 pout.close()
171 pout.close()
172 w.join()
172 w.join()
173 return f
173 return f
174
174
175 def tempfilter(s, cmd):
175 def tempfilter(s, cmd):
176 '''filter string S through a pair of temporary files with CMD.
176 '''filter string S through a pair of temporary files with CMD.
177 CMD is used as a template to create the real command to be run,
177 CMD is used as a template to create the real command to be run,
178 with the strings INFILE and OUTFILE replaced by the real names of
178 with the strings INFILE and OUTFILE replaced by the real names of
179 the temporary files generated.'''
179 the temporary files generated.'''
180 inname, outname = None, None
180 inname, outname = None, None
181 try:
181 try:
182 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
182 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
183 fp = os.fdopen(infd, 'wb')
183 fp = os.fdopen(infd, 'wb')
184 fp.write(s)
184 fp.write(s)
185 fp.close()
185 fp.close()
186 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
186 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
187 os.close(outfd)
187 os.close(outfd)
188 cmd = cmd.replace('INFILE', inname)
188 cmd = cmd.replace('INFILE', inname)
189 cmd = cmd.replace('OUTFILE', outname)
189 cmd = cmd.replace('OUTFILE', outname)
190 code = os.system(cmd)
190 code = os.system(cmd)
191 if code: raise Abort(_("command '%s' failed: %s") %
191 if code: raise Abort(_("command '%s' failed: %s") %
192 (cmd, explain_exit(code)))
192 (cmd, explain_exit(code)))
193 return open(outname, 'rb').read()
193 return open(outname, 'rb').read()
194 finally:
194 finally:
195 try:
195 try:
196 if inname: os.unlink(inname)
196 if inname: os.unlink(inname)
197 except: pass
197 except: pass
198 try:
198 try:
199 if outname: os.unlink(outname)
199 if outname: os.unlink(outname)
200 except: pass
200 except: pass
201
201
202 filtertable = {
202 filtertable = {
203 'tempfile:': tempfilter,
203 'tempfile:': tempfilter,
204 'pipe:': pipefilter,
204 'pipe:': pipefilter,
205 }
205 }
206
206
207 def filter(s, cmd):
207 def filter(s, cmd):
208 "filter a string through a command that transforms its input to its output"
208 "filter a string through a command that transforms its input to its output"
209 for name, fn in filtertable.iteritems():
209 for name, fn in filtertable.iteritems():
210 if cmd.startswith(name):
210 if cmd.startswith(name):
211 return fn(s, cmd[len(name):].lstrip())
211 return fn(s, cmd[len(name):].lstrip())
212 return pipefilter(s, cmd)
212 return pipefilter(s, cmd)
213
213
214 def find_in_path(name, path, default=None):
214 def find_in_path(name, path, default=None):
215 '''find name in search path. path can be string (will be split
215 '''find name in search path. path can be string (will be split
216 with os.pathsep), or iterable thing that returns strings. if name
216 with os.pathsep), or iterable thing that returns strings. if name
217 found, return path to name. else return default.'''
217 found, return path to name. else return default.'''
218 if isinstance(path, str):
218 if isinstance(path, str):
219 path = path.split(os.pathsep)
219 path = path.split(os.pathsep)
220 for p in path:
220 for p in path:
221 p_name = os.path.join(p, name)
221 p_name = os.path.join(p, name)
222 if os.path.exists(p_name):
222 if os.path.exists(p_name):
223 return p_name
223 return p_name
224 return default
224 return default
225
225
226 def binary(s):
226 def binary(s):
227 """return true if a string is binary data using diff's heuristic"""
227 """return true if a string is binary data using diff's heuristic"""
228 if s and '\0' in s[:4096]:
228 if s and '\0' in s[:4096]:
229 return True
229 return True
230 return False
230 return False
231
231
232 def unique(g):
232 def unique(g):
233 """return the uniq elements of iterable g"""
233 """return the uniq elements of iterable g"""
234 seen = {}
234 seen = {}
235 l = []
235 l = []
236 for f in g:
236 for f in g:
237 if f not in seen:
237 if f not in seen:
238 seen[f] = 1
238 seen[f] = 1
239 l.append(f)
239 l.append(f)
240 return l
240 return l
241
241
242 class Abort(Exception):
242 class Abort(Exception):
243 """Raised if a command needs to print an error and exit."""
243 """Raised if a command needs to print an error and exit."""
244
244
245 class UnexpectedOutput(Abort):
245 class UnexpectedOutput(Abort):
246 """Raised to print an error with part of output and exit."""
246 """Raised to print an error with part of output and exit."""
247
247
248 def always(fn): return True
248 def always(fn): return True
249 def never(fn): return False
249 def never(fn): return False
250
250
251 def expand_glob(pats):
251 def expand_glob(pats):
252 '''On Windows, expand the implicit globs in a list of patterns'''
252 '''On Windows, expand the implicit globs in a list of patterns'''
253 if os.name != 'nt':
253 if os.name != 'nt':
254 return list(pats)
254 return list(pats)
255 ret = []
255 ret = []
256 for p in pats:
256 for p in pats:
257 kind, name = patkind(p, None)
257 kind, name = patkind(p, None)
258 if kind is None:
258 if kind is None:
259 globbed = glob.glob(name)
259 globbed = glob.glob(name)
260 if globbed:
260 if globbed:
261 ret.extend(globbed)
261 ret.extend(globbed)
262 continue
262 continue
263 # if we couldn't expand the glob, just keep it around
263 # if we couldn't expand the glob, just keep it around
264 ret.append(p)
264 ret.append(p)
265 return ret
265 return ret
266
266
267 def patkind(name, dflt_pat='glob'):
267 def patkind(name, dflt_pat='glob'):
268 """Split a string into an optional pattern kind prefix and the
268 """Split a string into an optional pattern kind prefix and the
269 actual pattern."""
269 actual pattern."""
270 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
270 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
271 if name.startswith(prefix + ':'): return name.split(':', 1)
271 if name.startswith(prefix + ':'): return name.split(':', 1)
272 return dflt_pat, name
272 return dflt_pat, name
273
273
274 def globre(pat, head='^', tail='$'):
274 def globre(pat, head='^', tail='$'):
275 "convert a glob pattern into a regexp"
275 "convert a glob pattern into a regexp"
276 i, n = 0, len(pat)
276 i, n = 0, len(pat)
277 res = ''
277 res = ''
278 group = False
278 group = False
279 def peek(): return i < n and pat[i]
279 def peek(): return i < n and pat[i]
280 while i < n:
280 while i < n:
281 c = pat[i]
281 c = pat[i]
282 i = i+1
282 i = i+1
283 if c == '*':
283 if c == '*':
284 if peek() == '*':
284 if peek() == '*':
285 i += 1
285 i += 1
286 res += '.*'
286 res += '.*'
287 else:
287 else:
288 res += '[^/]*'
288 res += '[^/]*'
289 elif c == '?':
289 elif c == '?':
290 res += '.'
290 res += '.'
291 elif c == '[':
291 elif c == '[':
292 j = i
292 j = i
293 if j < n and pat[j] in '!]':
293 if j < n and pat[j] in '!]':
294 j += 1
294 j += 1
295 while j < n and pat[j] != ']':
295 while j < n and pat[j] != ']':
296 j += 1
296 j += 1
297 if j >= n:
297 if j >= n:
298 res += '\\['
298 res += '\\['
299 else:
299 else:
300 stuff = pat[i:j].replace('\\','\\\\')
300 stuff = pat[i:j].replace('\\','\\\\')
301 i = j + 1
301 i = j + 1
302 if stuff[0] == '!':
302 if stuff[0] == '!':
303 stuff = '^' + stuff[1:]
303 stuff = '^' + stuff[1:]
304 elif stuff[0] == '^':
304 elif stuff[0] == '^':
305 stuff = '\\' + stuff
305 stuff = '\\' + stuff
306 res = '%s[%s]' % (res, stuff)
306 res = '%s[%s]' % (res, stuff)
307 elif c == '{':
307 elif c == '{':
308 group = True
308 group = True
309 res += '(?:'
309 res += '(?:'
310 elif c == '}' and group:
310 elif c == '}' and group:
311 res += ')'
311 res += ')'
312 group = False
312 group = False
313 elif c == ',' and group:
313 elif c == ',' and group:
314 res += '|'
314 res += '|'
315 elif c == '\\':
315 elif c == '\\':
316 p = peek()
316 p = peek()
317 if p:
317 if p:
318 i += 1
318 i += 1
319 res += re.escape(p)
319 res += re.escape(p)
320 else:
320 else:
321 res += re.escape(c)
321 res += re.escape(c)
322 else:
322 else:
323 res += re.escape(c)
323 res += re.escape(c)
324 return head + res + tail
324 return head + res + tail
325
325
326 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
326 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
327
327
328 def pathto(root, n1, n2):
328 def pathto(root, n1, n2):
329 '''return the relative path from one place to another.
329 '''return the relative path from one place to another.
330 root should use os.sep to separate directories
330 root should use os.sep to separate directories
331 n1 should use os.sep to separate directories
331 n1 should use os.sep to separate directories
332 n2 should use "/" to separate directories
332 n2 should use "/" to separate directories
333 returns an os.sep-separated path.
333 returns an os.sep-separated path.
334
334
335 If n1 is a relative path, it's assumed it's
335 If n1 is a relative path, it's assumed it's
336 relative to root.
336 relative to root.
337 n2 should always be relative to root.
337 n2 should always be relative to root.
338 '''
338 '''
339 if not n1: return localpath(n2)
339 if not n1: return localpath(n2)
340 if os.path.isabs(n1):
340 if os.path.isabs(n1):
341 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
341 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
342 return os.path.join(root, localpath(n2))
342 return os.path.join(root, localpath(n2))
343 n2 = '/'.join((pconvert(root), n2))
343 n2 = '/'.join((pconvert(root), n2))
344 a, b = n1.split(os.sep), n2.split('/')
344 a, b = n1.split(os.sep), n2.split('/')
345 a.reverse()
345 a.reverse()
346 b.reverse()
346 b.reverse()
347 while a and b and a[-1] == b[-1]:
347 while a and b and a[-1] == b[-1]:
348 a.pop()
348 a.pop()
349 b.pop()
349 b.pop()
350 b.reverse()
350 b.reverse()
351 return os.sep.join((['..'] * len(a)) + b)
351 return os.sep.join((['..'] * len(a)) + b)
352
352
353 def canonpath(root, cwd, myname):
353 def canonpath(root, cwd, myname):
354 """return the canonical path of myname, given cwd and root"""
354 """return the canonical path of myname, given cwd and root"""
355 if root == os.sep:
355 if root == os.sep:
356 rootsep = os.sep
356 rootsep = os.sep
357 elif root.endswith(os.sep):
357 elif root.endswith(os.sep):
358 rootsep = root
358 rootsep = root
359 else:
359 else:
360 rootsep = root + os.sep
360 rootsep = root + os.sep
361 name = myname
361 name = myname
362 if not os.path.isabs(name):
362 if not os.path.isabs(name):
363 name = os.path.join(root, cwd, name)
363 name = os.path.join(root, cwd, name)
364 name = os.path.normpath(name)
364 name = os.path.normpath(name)
365 if name != rootsep and name.startswith(rootsep):
365 if name != rootsep and name.startswith(rootsep):
366 name = name[len(rootsep):]
366 name = name[len(rootsep):]
367 audit_path(name)
367 audit_path(name)
368 return pconvert(name)
368 return pconvert(name)
369 elif name == root:
369 elif name == root:
370 return ''
370 return ''
371 else:
371 else:
372 # Determine whether `name' is in the hierarchy at or beneath `root',
372 # Determine whether `name' is in the hierarchy at or beneath `root',
373 # by iterating name=dirname(name) until that causes no change (can't
373 # by iterating name=dirname(name) until that causes no change (can't
374 # check name == '/', because that doesn't work on windows). For each
374 # check name == '/', because that doesn't work on windows). For each
375 # `name', compare dev/inode numbers. If they match, the list `rel'
375 # `name', compare dev/inode numbers. If they match, the list `rel'
376 # holds the reversed list of components making up the relative file
376 # holds the reversed list of components making up the relative file
377 # name we want.
377 # name we want.
378 root_st = os.stat(root)
378 root_st = os.stat(root)
379 rel = []
379 rel = []
380 while True:
380 while True:
381 try:
381 try:
382 name_st = os.stat(name)
382 name_st = os.stat(name)
383 except OSError:
383 except OSError:
384 break
384 break
385 if samestat(name_st, root_st):
385 if samestat(name_st, root_st):
386 if not rel:
386 if not rel:
387 # name was actually the same as root (maybe a symlink)
387 # name was actually the same as root (maybe a symlink)
388 return ''
388 return ''
389 rel.reverse()
389 rel.reverse()
390 name = os.path.join(*rel)
390 name = os.path.join(*rel)
391 audit_path(name)
391 audit_path(name)
392 return pconvert(name)
392 return pconvert(name)
393 dirname, basename = os.path.split(name)
393 dirname, basename = os.path.split(name)
394 rel.append(basename)
394 rel.append(basename)
395 if dirname == name:
395 if dirname == name:
396 break
396 break
397 name = dirname
397 name = dirname
398
398
399 raise Abort('%s not under root' % myname)
399 raise Abort('%s not under root' % myname)
400
400
401 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None):
401 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None):
402 return _matcher(canonroot, cwd, names, inc, exc, 'glob', src)
402 return _matcher(canonroot, cwd, names, inc, exc, 'glob', src)
403
403
404 def cmdmatcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None,
404 def cmdmatcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None,
405 globbed=False, default=None):
405 globbed=False, default=None):
406 default = default or 'relpath'
406 default = default or 'relpath'
407 if default == 'relpath' and not globbed:
407 if default == 'relpath' and not globbed:
408 names = expand_glob(names)
408 names = expand_glob(names)
409 return _matcher(canonroot, cwd, names, inc, exc, default, src)
409 return _matcher(canonroot, cwd, names, inc, exc, default, src)
410
410
411 def _matcher(canonroot, cwd, names, inc, exc, dflt_pat, src):
411 def _matcher(canonroot, cwd, names, inc, exc, dflt_pat, src):
412 """build a function to match a set of file patterns
412 """build a function to match a set of file patterns
413
413
414 arguments:
414 arguments:
415 canonroot - the canonical root of the tree you're matching against
415 canonroot - the canonical root of the tree you're matching against
416 cwd - the current working directory, if relevant
416 cwd - the current working directory, if relevant
417 names - patterns to find
417 names - patterns to find
418 inc - patterns to include
418 inc - patterns to include
419 exc - patterns to exclude
419 exc - patterns to exclude
420 dflt_pat - if a pattern in names has no explicit type, assume this one
420 dflt_pat - if a pattern in names has no explicit type, assume this one
421 src - where these patterns came from (e.g. .hgignore)
421 src - where these patterns came from (e.g. .hgignore)
422
422
423 a pattern is one of:
423 a pattern is one of:
424 'glob:<glob>' - a glob relative to cwd
424 'glob:<glob>' - a glob relative to cwd
425 're:<regexp>' - a regular expression
425 're:<regexp>' - a regular expression
426 'path:<path>' - a path relative to canonroot
426 'path:<path>' - a path relative to canonroot
427 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
427 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
428 'relpath:<path>' - a path relative to cwd
428 'relpath:<path>' - a path relative to cwd
429 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
429 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
430 '<something>' - one of the cases above, selected by the dflt_pat argument
430 '<something>' - one of the cases above, selected by the dflt_pat argument
431
431
432 returns:
432 returns:
433 a 3-tuple containing
433 a 3-tuple containing
434 - list of roots (places where one should start a recursive walk of the fs);
434 - list of roots (places where one should start a recursive walk of the fs);
435 this often matches the explicit non-pattern names passed in, but also
435 this often matches the explicit non-pattern names passed in, but also
436 includes the initial part of glob: patterns that has no glob characters
436 includes the initial part of glob: patterns that has no glob characters
437 - a bool match(filename) function
437 - a bool match(filename) function
438 - a bool indicating if any patterns were passed in
438 - a bool indicating if any patterns were passed in
439 """
439 """
440
440
441 # a common case: no patterns at all
441 # a common case: no patterns at all
442 if not names and not inc and not exc:
442 if not names and not inc and not exc:
443 return [], always, False
443 return [], always, False
444
444
445 def contains_glob(name):
445 def contains_glob(name):
446 for c in name:
446 for c in name:
447 if c in _globchars: return True
447 if c in _globchars: return True
448 return False
448 return False
449
449
450 def regex(kind, name, tail):
450 def regex(kind, name):
451 '''convert a pattern into a regular expression'''
451 '''convert a pattern into a regular expression'''
452 if not name:
452 if not name:
453 return ''
453 return ''
454 if kind == 're':
454 if kind == 're':
455 return name
455 return name
456 elif kind == 'path':
456 elif kind == 'path':
457 return '^' + re.escape(name) + '(?:/|$)'
457 return '^' + re.escape(name) + '(?:/|$)'
458 elif kind == 'relglob':
458 elif kind == 'relglob':
459 return globre(name, '(?:|.*/)', '(?:/|$)')
459 return globre(name, '(?:|.*/)', '(?:/|$)')
460 elif kind == 'relpath':
460 elif kind == 'relpath':
461 return re.escape(name) + '(?:/|$)'
461 return re.escape(name) + '(?:/|$)'
462 elif kind == 'relre':
462 elif kind == 'relre':
463 if name.startswith('^'):
463 if name.startswith('^'):
464 return name
464 return name
465 return '.*' + name
465 return '.*' + name
466 return globre(name, '', tail)
466 return globre(name, '', '(?:/|$)')
467
467
468 def matchfn(pats, tail):
468 def matchfn(pats):
469 """build a matching function from a set of patterns"""
469 """build a matching function from a set of patterns"""
470 if not pats:
470 if not pats:
471 return
471 return
472 matches = []
472 matches = []
473 for k, p in pats:
473 for k, p in pats:
474 try:
474 try:
475 pat = '(?:%s)' % regex(k, p, tail)
475 pat = '(?:%s)' % regex(k, p)
476 matches.append(re.compile(pat).match)
476 matches.append(re.compile(pat).match)
477 except re.error:
477 except re.error:
478 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
478 if src: raise Abort("%s: invalid pattern (%s): %s" % (src, k, p))
479 else: raise Abort("invalid pattern (%s): %s" % (k, p))
479 else: raise Abort("invalid pattern (%s): %s" % (k, p))
480
480
481 def buildfn(text):
481 def buildfn(text):
482 for m in matches:
482 for m in matches:
483 r = m(text)
483 r = m(text)
484 if r:
484 if r:
485 return r
485 return r
486
486
487 return buildfn
487 return buildfn
488
488
489 def globprefix(pat):
489 def globprefix(pat):
490 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
490 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
491 root = []
491 root = []
492 for p in pat.split('/'):
492 for p in pat.split('/'):
493 if contains_glob(p): break
493 if contains_glob(p): break
494 root.append(p)
494 root.append(p)
495 return '/'.join(root) or '.'
495 return '/'.join(root) or '.'
496
496
497 def normalizepats(names, default):
497 def normalizepats(names, default):
498 pats = []
498 pats = []
499 roots = []
499 roots = []
500 anypats = False
500 anypats = False
501 for kind, name in [patkind(p, default) for p in names]:
501 for kind, name in [patkind(p, default) for p in names]:
502 if kind in ('glob', 'relpath'):
502 if kind in ('glob', 'relpath'):
503 name = canonpath(canonroot, cwd, name)
503 name = canonpath(canonroot, cwd, name)
504 elif kind in ('relglob', 'path'):
504 elif kind in ('relglob', 'path'):
505 name = normpath(name)
505 name = normpath(name)
506
506
507 pats.append((kind, name))
507 pats.append((kind, name))
508
508
509 if kind in ('glob', 're', 'relglob', 'relre'):
509 if kind in ('glob', 're', 'relglob', 'relre'):
510 anypats = True
510 anypats = True
511
511
512 if kind == 'glob':
512 if kind == 'glob':
513 root = globprefix(name)
513 root = globprefix(name)
514 roots.append(root)
514 roots.append(root)
515 elif kind in ('relpath', 'path'):
515 elif kind in ('relpath', 'path'):
516 roots.append(name or '.')
516 roots.append(name or '.')
517 elif kind == 'relglob':
517 elif kind == 'relglob':
518 roots.append('.')
518 roots.append('.')
519 return roots, pats, anypats
519 return roots, pats, anypats
520
520
521 roots, pats, anypats = normalizepats(names, dflt_pat)
521 roots, pats, anypats = normalizepats(names, dflt_pat)
522
522
523 patmatch = matchfn(pats, '$') or always
523 patmatch = matchfn(pats) or always
524 incmatch = always
524 incmatch = always
525 if inc:
525 if inc:
526 dummy, inckinds, dummy = normalizepats(inc, 'glob')
526 dummy, inckinds, dummy = normalizepats(inc, 'glob')
527 incmatch = matchfn(inckinds, '(?:/|$)')
527 incmatch = matchfn(inckinds)
528 excmatch = lambda fn: False
528 excmatch = lambda fn: False
529 if exc:
529 if exc:
530 dummy, exckinds, dummy = normalizepats(exc, 'glob')
530 dummy, exckinds, dummy = normalizepats(exc, 'glob')
531 excmatch = matchfn(exckinds, '(?:/|$)')
531 excmatch = matchfn(exckinds)
532
532
533 if not names and inc and not exc:
533 if not names and inc and not exc:
534 # common case: hgignore patterns
534 # common case: hgignore patterns
535 match = incmatch
535 match = incmatch
536 else:
536 else:
537 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
537 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
538
538
539 return (roots, match, (inc or exc or anypats) and True)
539 return (roots, match, (inc or exc or anypats) and True)
540
540
541 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
541 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
542 '''enhanced shell command execution.
542 '''enhanced shell command execution.
543 run with environment maybe modified, maybe in different dir.
543 run with environment maybe modified, maybe in different dir.
544
544
545 if command fails and onerr is None, return status. if ui object,
545 if command fails and onerr is None, return status. if ui object,
546 print error message and return status, else raise onerr object as
546 print error message and return status, else raise onerr object as
547 exception.'''
547 exception.'''
548 def py2shell(val):
548 def py2shell(val):
549 'convert python object into string that is useful to shell'
549 'convert python object into string that is useful to shell'
550 if val in (None, False):
550 if val in (None, False):
551 return '0'
551 return '0'
552 if val == True:
552 if val == True:
553 return '1'
553 return '1'
554 return str(val)
554 return str(val)
555 oldenv = {}
555 oldenv = {}
556 for k in environ:
556 for k in environ:
557 oldenv[k] = os.environ.get(k)
557 oldenv[k] = os.environ.get(k)
558 if cwd is not None:
558 if cwd is not None:
559 oldcwd = os.getcwd()
559 oldcwd = os.getcwd()
560 origcmd = cmd
560 origcmd = cmd
561 if os.name == 'nt':
561 if os.name == 'nt':
562 cmd = '"%s"' % cmd
562 cmd = '"%s"' % cmd
563 try:
563 try:
564 for k, v in environ.iteritems():
564 for k, v in environ.iteritems():
565 os.environ[k] = py2shell(v)
565 os.environ[k] = py2shell(v)
566 if cwd is not None and oldcwd != cwd:
566 if cwd is not None and oldcwd != cwd:
567 os.chdir(cwd)
567 os.chdir(cwd)
568 rc = os.system(cmd)
568 rc = os.system(cmd)
569 if rc and onerr:
569 if rc and onerr:
570 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
570 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
571 explain_exit(rc)[0])
571 explain_exit(rc)[0])
572 if errprefix:
572 if errprefix:
573 errmsg = '%s: %s' % (errprefix, errmsg)
573 errmsg = '%s: %s' % (errprefix, errmsg)
574 try:
574 try:
575 onerr.warn(errmsg + '\n')
575 onerr.warn(errmsg + '\n')
576 except AttributeError:
576 except AttributeError:
577 raise onerr(errmsg)
577 raise onerr(errmsg)
578 return rc
578 return rc
579 finally:
579 finally:
580 for k, v in oldenv.iteritems():
580 for k, v in oldenv.iteritems():
581 if v is None:
581 if v is None:
582 del os.environ[k]
582 del os.environ[k]
583 else:
583 else:
584 os.environ[k] = v
584 os.environ[k] = v
585 if cwd is not None and oldcwd != cwd:
585 if cwd is not None and oldcwd != cwd:
586 os.chdir(oldcwd)
586 os.chdir(oldcwd)
587
587
588 def rename(src, dst):
588 def rename(src, dst):
589 """forcibly rename a file"""
589 """forcibly rename a file"""
590 try:
590 try:
591 os.rename(src, dst)
591 os.rename(src, dst)
592 except OSError, err:
592 except OSError, err:
593 # on windows, rename to existing file is not allowed, so we
593 # on windows, rename to existing file is not allowed, so we
594 # must delete destination first. but if file is open, unlink
594 # must delete destination first. but if file is open, unlink
595 # schedules it for delete but does not delete it. rename
595 # schedules it for delete but does not delete it. rename
596 # happens immediately even for open files, so we create
596 # happens immediately even for open files, so we create
597 # temporary file, delete it, rename destination to that name,
597 # temporary file, delete it, rename destination to that name,
598 # then delete that. then rename is safe to do.
598 # then delete that. then rename is safe to do.
599 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
599 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
600 os.close(fd)
600 os.close(fd)
601 os.unlink(temp)
601 os.unlink(temp)
602 os.rename(dst, temp)
602 os.rename(dst, temp)
603 os.unlink(temp)
603 os.unlink(temp)
604 os.rename(src, dst)
604 os.rename(src, dst)
605
605
606 def unlink(f):
606 def unlink(f):
607 """unlink and remove the directory if it is empty"""
607 """unlink and remove the directory if it is empty"""
608 os.unlink(f)
608 os.unlink(f)
609 # try removing directories that might now be empty
609 # try removing directories that might now be empty
610 try:
610 try:
611 os.removedirs(os.path.dirname(f))
611 os.removedirs(os.path.dirname(f))
612 except OSError:
612 except OSError:
613 pass
613 pass
614
614
615 def copyfile(src, dest):
615 def copyfile(src, dest):
616 "copy a file, preserving mode"
616 "copy a file, preserving mode"
617 try:
617 try:
618 shutil.copyfile(src, dest)
618 shutil.copyfile(src, dest)
619 shutil.copymode(src, dest)
619 shutil.copymode(src, dest)
620 except shutil.Error, inst:
620 except shutil.Error, inst:
621 raise Abort(str(inst))
621 raise Abort(str(inst))
622
622
623 def copyfiles(src, dst, hardlink=None):
623 def copyfiles(src, dst, hardlink=None):
624 """Copy a directory tree using hardlinks if possible"""
624 """Copy a directory tree using hardlinks if possible"""
625
625
626 if hardlink is None:
626 if hardlink is None:
627 hardlink = (os.stat(src).st_dev ==
627 hardlink = (os.stat(src).st_dev ==
628 os.stat(os.path.dirname(dst)).st_dev)
628 os.stat(os.path.dirname(dst)).st_dev)
629
629
630 if os.path.isdir(src):
630 if os.path.isdir(src):
631 os.mkdir(dst)
631 os.mkdir(dst)
632 for name in os.listdir(src):
632 for name in os.listdir(src):
633 srcname = os.path.join(src, name)
633 srcname = os.path.join(src, name)
634 dstname = os.path.join(dst, name)
634 dstname = os.path.join(dst, name)
635 copyfiles(srcname, dstname, hardlink)
635 copyfiles(srcname, dstname, hardlink)
636 else:
636 else:
637 if hardlink:
637 if hardlink:
638 try:
638 try:
639 os_link(src, dst)
639 os_link(src, dst)
640 except (IOError, OSError):
640 except (IOError, OSError):
641 hardlink = False
641 hardlink = False
642 shutil.copy(src, dst)
642 shutil.copy(src, dst)
643 else:
643 else:
644 shutil.copy(src, dst)
644 shutil.copy(src, dst)
645
645
646 def audit_path(path):
646 def audit_path(path):
647 """Abort if path contains dangerous components"""
647 """Abort if path contains dangerous components"""
648 parts = os.path.normcase(path).split(os.sep)
648 parts = os.path.normcase(path).split(os.sep)
649 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
649 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
650 or os.pardir in parts):
650 or os.pardir in parts):
651 raise Abort(_("path contains illegal component: %s\n") % path)
651 raise Abort(_("path contains illegal component: %s\n") % path)
652
652
653 def _makelock_file(info, pathname):
653 def _makelock_file(info, pathname):
654 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
654 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
655 os.write(ld, info)
655 os.write(ld, info)
656 os.close(ld)
656 os.close(ld)
657
657
658 def _readlock_file(pathname):
658 def _readlock_file(pathname):
659 return posixfile(pathname).read()
659 return posixfile(pathname).read()
660
660
661 def nlinks(pathname):
661 def nlinks(pathname):
662 """Return number of hardlinks for the given file."""
662 """Return number of hardlinks for the given file."""
663 return os.lstat(pathname).st_nlink
663 return os.lstat(pathname).st_nlink
664
664
665 if hasattr(os, 'link'):
665 if hasattr(os, 'link'):
666 os_link = os.link
666 os_link = os.link
667 else:
667 else:
668 def os_link(src, dst):
668 def os_link(src, dst):
669 raise OSError(0, _("Hardlinks not supported"))
669 raise OSError(0, _("Hardlinks not supported"))
670
670
671 def fstat(fp):
671 def fstat(fp):
672 '''stat file object that may not have fileno method.'''
672 '''stat file object that may not have fileno method.'''
673 try:
673 try:
674 return os.fstat(fp.fileno())
674 return os.fstat(fp.fileno())
675 except AttributeError:
675 except AttributeError:
676 return os.stat(fp.name)
676 return os.stat(fp.name)
677
677
678 posixfile = file
678 posixfile = file
679
679
680 def is_win_9x():
680 def is_win_9x():
681 '''return true if run on windows 95, 98 or me.'''
681 '''return true if run on windows 95, 98 or me.'''
682 try:
682 try:
683 return sys.getwindowsversion()[3] == 1
683 return sys.getwindowsversion()[3] == 1
684 except AttributeError:
684 except AttributeError:
685 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
685 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
686
686
687 getuser_fallback = None
687 getuser_fallback = None
688
688
689 def getuser():
689 def getuser():
690 '''return name of current user'''
690 '''return name of current user'''
691 try:
691 try:
692 return getpass.getuser()
692 return getpass.getuser()
693 except ImportError:
693 except ImportError:
694 # import of pwd will fail on windows - try fallback
694 # import of pwd will fail on windows - try fallback
695 if getuser_fallback:
695 if getuser_fallback:
696 return getuser_fallback()
696 return getuser_fallback()
697 # raised if win32api not available
697 # raised if win32api not available
698 raise Abort(_('user name not available - set USERNAME '
698 raise Abort(_('user name not available - set USERNAME '
699 'environment variable'))
699 'environment variable'))
700
700
701 def username(uid=None):
701 def username(uid=None):
702 """Return the name of the user with the given uid.
702 """Return the name of the user with the given uid.
703
703
704 If uid is None, return the name of the current user."""
704 If uid is None, return the name of the current user."""
705 try:
705 try:
706 import pwd
706 import pwd
707 if uid is None:
707 if uid is None:
708 uid = os.getuid()
708 uid = os.getuid()
709 try:
709 try:
710 return pwd.getpwuid(uid)[0]
710 return pwd.getpwuid(uid)[0]
711 except KeyError:
711 except KeyError:
712 return str(uid)
712 return str(uid)
713 except ImportError:
713 except ImportError:
714 return None
714 return None
715
715
716 def groupname(gid=None):
716 def groupname(gid=None):
717 """Return the name of the group with the given gid.
717 """Return the name of the group with the given gid.
718
718
719 If gid is None, return the name of the current group."""
719 If gid is None, return the name of the current group."""
720 try:
720 try:
721 import grp
721 import grp
722 if gid is None:
722 if gid is None:
723 gid = os.getgid()
723 gid = os.getgid()
724 try:
724 try:
725 return grp.getgrgid(gid)[0]
725 return grp.getgrgid(gid)[0]
726 except KeyError:
726 except KeyError:
727 return str(gid)
727 return str(gid)
728 except ImportError:
728 except ImportError:
729 return None
729 return None
730
730
731 # File system features
731 # File system features
732
732
733 def checkfolding(path):
733 def checkfolding(path):
734 """
734 """
735 Check whether the given path is on a case-sensitive filesystem
735 Check whether the given path is on a case-sensitive filesystem
736
736
737 Requires a path (like /foo/.hg) ending with a foldable final
737 Requires a path (like /foo/.hg) ending with a foldable final
738 directory component.
738 directory component.
739 """
739 """
740 s1 = os.stat(path)
740 s1 = os.stat(path)
741 d, b = os.path.split(path)
741 d, b = os.path.split(path)
742 p2 = os.path.join(d, b.upper())
742 p2 = os.path.join(d, b.upper())
743 if path == p2:
743 if path == p2:
744 p2 = os.path.join(d, b.lower())
744 p2 = os.path.join(d, b.lower())
745 try:
745 try:
746 s2 = os.stat(p2)
746 s2 = os.stat(p2)
747 if s2 == s1:
747 if s2 == s1:
748 return False
748 return False
749 return True
749 return True
750 except:
750 except:
751 return True
751 return True
752
752
753 def checkexec(path):
753 def checkexec(path):
754 """
754 """
755 Check whether the given path is on a filesystem with UNIX-like exec flags
755 Check whether the given path is on a filesystem with UNIX-like exec flags
756
756
757 Requires a directory (like /foo/.hg)
757 Requires a directory (like /foo/.hg)
758 """
758 """
759 fh, fn = tempfile.mkstemp("", "", path)
759 fh, fn = tempfile.mkstemp("", "", path)
760 os.close(fh)
760 os.close(fh)
761 m = os.stat(fn).st_mode
761 m = os.stat(fn).st_mode
762 os.chmod(fn, m ^ 0111)
762 os.chmod(fn, m ^ 0111)
763 r = (os.stat(fn).st_mode != m)
763 r = (os.stat(fn).st_mode != m)
764 os.unlink(fn)
764 os.unlink(fn)
765 return r
765 return r
766
766
767 def execfunc(path, fallback):
767 def execfunc(path, fallback):
768 '''return an is_exec() function with default to fallback'''
768 '''return an is_exec() function with default to fallback'''
769 if checkexec(path):
769 if checkexec(path):
770 return lambda x: is_exec(os.path.join(path, x))
770 return lambda x: is_exec(os.path.join(path, x))
771 return fallback
771 return fallback
772
772
773 def checklink(path):
773 def checklink(path):
774 """check whether the given path is on a symlink-capable filesystem"""
774 """check whether the given path is on a symlink-capable filesystem"""
775 # mktemp is not racy because symlink creation will fail if the
775 # mktemp is not racy because symlink creation will fail if the
776 # file already exists
776 # file already exists
777 name = tempfile.mktemp(dir=path)
777 name = tempfile.mktemp(dir=path)
778 try:
778 try:
779 os.symlink(".", name)
779 os.symlink(".", name)
780 os.unlink(name)
780 os.unlink(name)
781 return True
781 return True
782 except (OSError, AttributeError):
782 except (OSError, AttributeError):
783 return False
783 return False
784
784
785 def linkfunc(path, fallback):
785 def linkfunc(path, fallback):
786 '''return an is_link() function with default to fallback'''
786 '''return an is_link() function with default to fallback'''
787 if checklink(path):
787 if checklink(path):
788 return lambda x: is_link(os.path.join(path, x))
788 return lambda x: is_link(os.path.join(path, x))
789 return fallback
789 return fallback
790
790
791 # Platform specific variants
791 # Platform specific variants
792 if os.name == 'nt':
792 if os.name == 'nt':
793 import msvcrt
793 import msvcrt
794 nulldev = 'NUL:'
794 nulldev = 'NUL:'
795
795
796 class winstdout:
796 class winstdout:
797 '''stdout on windows misbehaves if sent through a pipe'''
797 '''stdout on windows misbehaves if sent through a pipe'''
798
798
799 def __init__(self, fp):
799 def __init__(self, fp):
800 self.fp = fp
800 self.fp = fp
801
801
802 def __getattr__(self, key):
802 def __getattr__(self, key):
803 return getattr(self.fp, key)
803 return getattr(self.fp, key)
804
804
805 def close(self):
805 def close(self):
806 try:
806 try:
807 self.fp.close()
807 self.fp.close()
808 except: pass
808 except: pass
809
809
810 def write(self, s):
810 def write(self, s):
811 try:
811 try:
812 return self.fp.write(s)
812 return self.fp.write(s)
813 except IOError, inst:
813 except IOError, inst:
814 if inst.errno != 0: raise
814 if inst.errno != 0: raise
815 self.close()
815 self.close()
816 raise IOError(errno.EPIPE, 'Broken pipe')
816 raise IOError(errno.EPIPE, 'Broken pipe')
817
817
818 def flush(self):
818 def flush(self):
819 try:
819 try:
820 return self.fp.flush()
820 return self.fp.flush()
821 except IOError, inst:
821 except IOError, inst:
822 if inst.errno != errno.EINVAL: raise
822 if inst.errno != errno.EINVAL: raise
823 self.close()
823 self.close()
824 raise IOError(errno.EPIPE, 'Broken pipe')
824 raise IOError(errno.EPIPE, 'Broken pipe')
825
825
826 sys.stdout = winstdout(sys.stdout)
826 sys.stdout = winstdout(sys.stdout)
827
827
828 def system_rcpath():
828 def system_rcpath():
829 try:
829 try:
830 return system_rcpath_win32()
830 return system_rcpath_win32()
831 except:
831 except:
832 return [r'c:\mercurial\mercurial.ini']
832 return [r'c:\mercurial\mercurial.ini']
833
833
834 def user_rcpath():
834 def user_rcpath():
835 '''return os-specific hgrc search path to the user dir'''
835 '''return os-specific hgrc search path to the user dir'''
836 try:
836 try:
837 userrc = user_rcpath_win32()
837 userrc = user_rcpath_win32()
838 except:
838 except:
839 userrc = os.path.join(os.path.expanduser('~'), 'mercurial.ini')
839 userrc = os.path.join(os.path.expanduser('~'), 'mercurial.ini')
840 path = [userrc]
840 path = [userrc]
841 userprofile = os.environ.get('USERPROFILE')
841 userprofile = os.environ.get('USERPROFILE')
842 if userprofile:
842 if userprofile:
843 path.append(os.path.join(userprofile, 'mercurial.ini'))
843 path.append(os.path.join(userprofile, 'mercurial.ini'))
844 return path
844 return path
845
845
846 def parse_patch_output(output_line):
846 def parse_patch_output(output_line):
847 """parses the output produced by patch and returns the file name"""
847 """parses the output produced by patch and returns the file name"""
848 pf = output_line[14:]
848 pf = output_line[14:]
849 if pf[0] == '`':
849 if pf[0] == '`':
850 pf = pf[1:-1] # Remove the quotes
850 pf = pf[1:-1] # Remove the quotes
851 return pf
851 return pf
852
852
853 def testpid(pid):
853 def testpid(pid):
854 '''return False if pid dead, True if running or not known'''
854 '''return False if pid dead, True if running or not known'''
855 return True
855 return True
856
856
857 def set_exec(f, mode):
857 def set_exec(f, mode):
858 pass
858 pass
859
859
860 def set_link(f, mode):
860 def set_link(f, mode):
861 pass
861 pass
862
862
863 def set_binary(fd):
863 def set_binary(fd):
864 msvcrt.setmode(fd.fileno(), os.O_BINARY)
864 msvcrt.setmode(fd.fileno(), os.O_BINARY)
865
865
866 def pconvert(path):
866 def pconvert(path):
867 return path.replace("\\", "/")
867 return path.replace("\\", "/")
868
868
869 def localpath(path):
869 def localpath(path):
870 return path.replace('/', '\\')
870 return path.replace('/', '\\')
871
871
872 def normpath(path):
872 def normpath(path):
873 return pconvert(os.path.normpath(path))
873 return pconvert(os.path.normpath(path))
874
874
875 makelock = _makelock_file
875 makelock = _makelock_file
876 readlock = _readlock_file
876 readlock = _readlock_file
877
877
878 def samestat(s1, s2):
878 def samestat(s1, s2):
879 return False
879 return False
880
880
881 # A sequence of backslashes is special iff it precedes a double quote:
881 # A sequence of backslashes is special iff it precedes a double quote:
882 # - if there's an even number of backslashes, the double quote is not
882 # - if there's an even number of backslashes, the double quote is not
883 # quoted (i.e. it ends the quoted region)
883 # quoted (i.e. it ends the quoted region)
884 # - if there's an odd number of backslashes, the double quote is quoted
884 # - if there's an odd number of backslashes, the double quote is quoted
885 # - in both cases, every pair of backslashes is unquoted into a single
885 # - in both cases, every pair of backslashes is unquoted into a single
886 # backslash
886 # backslash
887 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
887 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
888 # So, to quote a string, we must surround it in double quotes, double
888 # So, to quote a string, we must surround it in double quotes, double
889 # the number of backslashes that preceed double quotes and add another
889 # the number of backslashes that preceed double quotes and add another
890 # backslash before every double quote (being careful with the double
890 # backslash before every double quote (being careful with the double
891 # quote we've appended to the end)
891 # quote we've appended to the end)
892 _quotere = None
892 _quotere = None
893 def shellquote(s):
893 def shellquote(s):
894 global _quotere
894 global _quotere
895 if _quotere is None:
895 if _quotere is None:
896 _quotere = re.compile(r'(\\*)("|\\$)')
896 _quotere = re.compile(r'(\\*)("|\\$)')
897 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
897 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
898
898
899 def explain_exit(code):
899 def explain_exit(code):
900 return _("exited with status %d") % code, code
900 return _("exited with status %d") % code, code
901
901
902 # if you change this stub into a real check, please try to implement the
902 # if you change this stub into a real check, please try to implement the
903 # username and groupname functions above, too.
903 # username and groupname functions above, too.
904 def isowner(fp, st=None):
904 def isowner(fp, st=None):
905 return True
905 return True
906
906
907 try:
907 try:
908 # override functions with win32 versions if possible
908 # override functions with win32 versions if possible
909 from util_win32 import *
909 from util_win32 import *
910 if not is_win_9x():
910 if not is_win_9x():
911 posixfile = posixfile_nt
911 posixfile = posixfile_nt
912 except ImportError:
912 except ImportError:
913 pass
913 pass
914
914
915 else:
915 else:
916 nulldev = '/dev/null'
916 nulldev = '/dev/null'
917 _umask = os.umask(0)
917 _umask = os.umask(0)
918 os.umask(_umask)
918 os.umask(_umask)
919
919
920 def rcfiles(path):
920 def rcfiles(path):
921 rcs = [os.path.join(path, 'hgrc')]
921 rcs = [os.path.join(path, 'hgrc')]
922 rcdir = os.path.join(path, 'hgrc.d')
922 rcdir = os.path.join(path, 'hgrc.d')
923 try:
923 try:
924 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
924 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
925 if f.endswith(".rc")])
925 if f.endswith(".rc")])
926 except OSError:
926 except OSError:
927 pass
927 pass
928 return rcs
928 return rcs
929
929
930 def system_rcpath():
930 def system_rcpath():
931 path = []
931 path = []
932 # old mod_python does not set sys.argv
932 # old mod_python does not set sys.argv
933 if len(getattr(sys, 'argv', [])) > 0:
933 if len(getattr(sys, 'argv', [])) > 0:
934 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
934 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
935 '/../etc/mercurial'))
935 '/../etc/mercurial'))
936 path.extend(rcfiles('/etc/mercurial'))
936 path.extend(rcfiles('/etc/mercurial'))
937 return path
937 return path
938
938
939 def user_rcpath():
939 def user_rcpath():
940 return [os.path.expanduser('~/.hgrc')]
940 return [os.path.expanduser('~/.hgrc')]
941
941
942 def parse_patch_output(output_line):
942 def parse_patch_output(output_line):
943 """parses the output produced by patch and returns the file name"""
943 """parses the output produced by patch and returns the file name"""
944 pf = output_line[14:]
944 pf = output_line[14:]
945 if pf.startswith("'") and pf.endswith("'") and " " in pf:
945 if pf.startswith("'") and pf.endswith("'") and " " in pf:
946 pf = pf[1:-1] # Remove the quotes
946 pf = pf[1:-1] # Remove the quotes
947 return pf
947 return pf
948
948
949 def is_exec(f):
949 def is_exec(f):
950 """check whether a file is executable"""
950 """check whether a file is executable"""
951 return (os.lstat(f).st_mode & 0100 != 0)
951 return (os.lstat(f).st_mode & 0100 != 0)
952
952
953 def set_exec(f, mode):
953 def set_exec(f, mode):
954 s = os.lstat(f).st_mode
954 s = os.lstat(f).st_mode
955 if (s & 0100 != 0) == mode:
955 if (s & 0100 != 0) == mode:
956 return
956 return
957 if mode:
957 if mode:
958 # Turn on +x for every +r bit when making a file executable
958 # Turn on +x for every +r bit when making a file executable
959 # and obey umask.
959 # and obey umask.
960 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
960 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
961 else:
961 else:
962 os.chmod(f, s & 0666)
962 os.chmod(f, s & 0666)
963
963
964 def is_link(f):
964 def is_link(f):
965 """check whether a file is a symlink"""
965 """check whether a file is a symlink"""
966 return (os.lstat(f).st_mode & 0120000 == 0120000)
966 return (os.lstat(f).st_mode & 0120000 == 0120000)
967
967
968 def set_link(f, mode):
968 def set_link(f, mode):
969 """make a file a symbolic link/regular file
969 """make a file a symbolic link/regular file
970
970
971 if a file is changed to a link, its contents become the link data
971 if a file is changed to a link, its contents become the link data
972 if a link is changed to a file, its link data become its contents
972 if a link is changed to a file, its link data become its contents
973 """
973 """
974
974
975 m = is_link(f)
975 m = is_link(f)
976 if m == bool(mode):
976 if m == bool(mode):
977 return
977 return
978
978
979 if mode: # switch file to link
979 if mode: # switch file to link
980 data = file(f).read()
980 data = file(f).read()
981 os.unlink(f)
981 os.unlink(f)
982 os.symlink(data, f)
982 os.symlink(data, f)
983 else:
983 else:
984 data = os.readlink(f)
984 data = os.readlink(f)
985 os.unlink(f)
985 os.unlink(f)
986 file(f, "w").write(data)
986 file(f, "w").write(data)
987
987
988 def set_binary(fd):
988 def set_binary(fd):
989 pass
989 pass
990
990
991 def pconvert(path):
991 def pconvert(path):
992 return path
992 return path
993
993
994 def localpath(path):
994 def localpath(path):
995 return path
995 return path
996
996
997 normpath = os.path.normpath
997 normpath = os.path.normpath
998 samestat = os.path.samestat
998 samestat = os.path.samestat
999
999
1000 def makelock(info, pathname):
1000 def makelock(info, pathname):
1001 try:
1001 try:
1002 os.symlink(info, pathname)
1002 os.symlink(info, pathname)
1003 except OSError, why:
1003 except OSError, why:
1004 if why.errno == errno.EEXIST:
1004 if why.errno == errno.EEXIST:
1005 raise
1005 raise
1006 else:
1006 else:
1007 _makelock_file(info, pathname)
1007 _makelock_file(info, pathname)
1008
1008
1009 def readlock(pathname):
1009 def readlock(pathname):
1010 try:
1010 try:
1011 return os.readlink(pathname)
1011 return os.readlink(pathname)
1012 except OSError, why:
1012 except OSError, why:
1013 if why.errno == errno.EINVAL:
1013 if why.errno == errno.EINVAL:
1014 return _readlock_file(pathname)
1014 return _readlock_file(pathname)
1015 else:
1015 else:
1016 raise
1016 raise
1017
1017
1018 def shellquote(s):
1018 def shellquote(s):
1019 return "'%s'" % s.replace("'", "'\\''")
1019 return "'%s'" % s.replace("'", "'\\''")
1020
1020
1021 def testpid(pid):
1021 def testpid(pid):
1022 '''return False if pid dead, True if running or not sure'''
1022 '''return False if pid dead, True if running or not sure'''
1023 try:
1023 try:
1024 os.kill(pid, 0)
1024 os.kill(pid, 0)
1025 return True
1025 return True
1026 except OSError, inst:
1026 except OSError, inst:
1027 return inst.errno != errno.ESRCH
1027 return inst.errno != errno.ESRCH
1028
1028
1029 def explain_exit(code):
1029 def explain_exit(code):
1030 """return a 2-tuple (desc, code) describing a process's status"""
1030 """return a 2-tuple (desc, code) describing a process's status"""
1031 if os.WIFEXITED(code):
1031 if os.WIFEXITED(code):
1032 val = os.WEXITSTATUS(code)
1032 val = os.WEXITSTATUS(code)
1033 return _("exited with status %d") % val, val
1033 return _("exited with status %d") % val, val
1034 elif os.WIFSIGNALED(code):
1034 elif os.WIFSIGNALED(code):
1035 val = os.WTERMSIG(code)
1035 val = os.WTERMSIG(code)
1036 return _("killed by signal %d") % val, val
1036 return _("killed by signal %d") % val, val
1037 elif os.WIFSTOPPED(code):
1037 elif os.WIFSTOPPED(code):
1038 val = os.WSTOPSIG(code)
1038 val = os.WSTOPSIG(code)
1039 return _("stopped by signal %d") % val, val
1039 return _("stopped by signal %d") % val, val
1040 raise ValueError(_("invalid exit code"))
1040 raise ValueError(_("invalid exit code"))
1041
1041
1042 def isowner(fp, st=None):
1042 def isowner(fp, st=None):
1043 """Return True if the file object f belongs to the current user.
1043 """Return True if the file object f belongs to the current user.
1044
1044
1045 The return value of a util.fstat(f) may be passed as the st argument.
1045 The return value of a util.fstat(f) may be passed as the st argument.
1046 """
1046 """
1047 if st is None:
1047 if st is None:
1048 st = fstat(fp)
1048 st = fstat(fp)
1049 return st.st_uid == os.getuid()
1049 return st.st_uid == os.getuid()
1050
1050
1051 def _buildencodefun():
1051 def _buildencodefun():
1052 e = '_'
1052 e = '_'
1053 win_reserved = [ord(x) for x in '\\:*?"<>|']
1053 win_reserved = [ord(x) for x in '\\:*?"<>|']
1054 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1054 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1055 for x in (range(32) + range(126, 256) + win_reserved):
1055 for x in (range(32) + range(126, 256) + win_reserved):
1056 cmap[chr(x)] = "~%02x" % x
1056 cmap[chr(x)] = "~%02x" % x
1057 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1057 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1058 cmap[chr(x)] = e + chr(x).lower()
1058 cmap[chr(x)] = e + chr(x).lower()
1059 dmap = {}
1059 dmap = {}
1060 for k, v in cmap.iteritems():
1060 for k, v in cmap.iteritems():
1061 dmap[v] = k
1061 dmap[v] = k
1062 def decode(s):
1062 def decode(s):
1063 i = 0
1063 i = 0
1064 while i < len(s):
1064 while i < len(s):
1065 for l in xrange(1, 4):
1065 for l in xrange(1, 4):
1066 try:
1066 try:
1067 yield dmap[s[i:i+l]]
1067 yield dmap[s[i:i+l]]
1068 i += l
1068 i += l
1069 break
1069 break
1070 except KeyError:
1070 except KeyError:
1071 pass
1071 pass
1072 else:
1072 else:
1073 raise KeyError
1073 raise KeyError
1074 return (lambda s: "".join([cmap[c] for c in s]),
1074 return (lambda s: "".join([cmap[c] for c in s]),
1075 lambda s: "".join(list(decode(s))))
1075 lambda s: "".join(list(decode(s))))
1076
1076
1077 encodefilename, decodefilename = _buildencodefun()
1077 encodefilename, decodefilename = _buildencodefun()
1078
1078
1079 def encodedopener(openerfn, fn):
1079 def encodedopener(openerfn, fn):
1080 def o(path, *args, **kw):
1080 def o(path, *args, **kw):
1081 return openerfn(fn(path), *args, **kw)
1081 return openerfn(fn(path), *args, **kw)
1082 return o
1082 return o
1083
1083
1084 def opener(base, audit=True):
1084 def opener(base, audit=True):
1085 """
1085 """
1086 return a function that opens files relative to base
1086 return a function that opens files relative to base
1087
1087
1088 this function is used to hide the details of COW semantics and
1088 this function is used to hide the details of COW semantics and
1089 remote file access from higher level code.
1089 remote file access from higher level code.
1090 """
1090 """
1091 p = base
1091 p = base
1092 audit_p = audit
1092 audit_p = audit
1093
1093
1094 def mktempcopy(name):
1094 def mktempcopy(name):
1095 d, fn = os.path.split(name)
1095 d, fn = os.path.split(name)
1096 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1096 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1097 os.close(fd)
1097 os.close(fd)
1098 ofp = posixfile(temp, "wb")
1098 ofp = posixfile(temp, "wb")
1099 try:
1099 try:
1100 try:
1100 try:
1101 ifp = posixfile(name, "rb")
1101 ifp = posixfile(name, "rb")
1102 except IOError, inst:
1102 except IOError, inst:
1103 if not getattr(inst, 'filename', None):
1103 if not getattr(inst, 'filename', None):
1104 inst.filename = name
1104 inst.filename = name
1105 raise
1105 raise
1106 for chunk in filechunkiter(ifp):
1106 for chunk in filechunkiter(ifp):
1107 ofp.write(chunk)
1107 ofp.write(chunk)
1108 ifp.close()
1108 ifp.close()
1109 ofp.close()
1109 ofp.close()
1110 except:
1110 except:
1111 try: os.unlink(temp)
1111 try: os.unlink(temp)
1112 except: pass
1112 except: pass
1113 raise
1113 raise
1114 st = os.lstat(name)
1114 st = os.lstat(name)
1115 os.chmod(temp, st.st_mode)
1115 os.chmod(temp, st.st_mode)
1116 return temp
1116 return temp
1117
1117
1118 class atomictempfile(posixfile):
1118 class atomictempfile(posixfile):
1119 """the file will only be copied when rename is called"""
1119 """the file will only be copied when rename is called"""
1120 def __init__(self, name, mode):
1120 def __init__(self, name, mode):
1121 self.__name = name
1121 self.__name = name
1122 self.temp = mktempcopy(name)
1122 self.temp = mktempcopy(name)
1123 posixfile.__init__(self, self.temp, mode)
1123 posixfile.__init__(self, self.temp, mode)
1124 def rename(self):
1124 def rename(self):
1125 if not self.closed:
1125 if not self.closed:
1126 posixfile.close(self)
1126 posixfile.close(self)
1127 rename(self.temp, localpath(self.__name))
1127 rename(self.temp, localpath(self.__name))
1128 def __del__(self):
1128 def __del__(self):
1129 if not self.closed:
1129 if not self.closed:
1130 try:
1130 try:
1131 os.unlink(self.temp)
1131 os.unlink(self.temp)
1132 except: pass
1132 except: pass
1133 posixfile.close(self)
1133 posixfile.close(self)
1134
1134
1135 class atomicfile(atomictempfile):
1135 class atomicfile(atomictempfile):
1136 """the file will only be copied on close"""
1136 """the file will only be copied on close"""
1137 def __init__(self, name, mode):
1137 def __init__(self, name, mode):
1138 atomictempfile.__init__(self, name, mode)
1138 atomictempfile.__init__(self, name, mode)
1139 def close(self):
1139 def close(self):
1140 self.rename()
1140 self.rename()
1141 def __del__(self):
1141 def __del__(self):
1142 self.rename()
1142 self.rename()
1143
1143
1144 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
1144 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
1145 if audit_p:
1145 if audit_p:
1146 audit_path(path)
1146 audit_path(path)
1147 f = os.path.join(p, path)
1147 f = os.path.join(p, path)
1148
1148
1149 if not text:
1149 if not text:
1150 mode += "b" # for that other OS
1150 mode += "b" # for that other OS
1151
1151
1152 if mode[0] != "r":
1152 if mode[0] != "r":
1153 try:
1153 try:
1154 nlink = nlinks(f)
1154 nlink = nlinks(f)
1155 except OSError:
1155 except OSError:
1156 d = os.path.dirname(f)
1156 d = os.path.dirname(f)
1157 if not os.path.isdir(d):
1157 if not os.path.isdir(d):
1158 os.makedirs(d)
1158 os.makedirs(d)
1159 else:
1159 else:
1160 if atomic:
1160 if atomic:
1161 return atomicfile(f, mode)
1161 return atomicfile(f, mode)
1162 elif atomictemp:
1162 elif atomictemp:
1163 return atomictempfile(f, mode)
1163 return atomictempfile(f, mode)
1164 if nlink > 1:
1164 if nlink > 1:
1165 rename(mktempcopy(f), f)
1165 rename(mktempcopy(f), f)
1166 return posixfile(f, mode)
1166 return posixfile(f, mode)
1167
1167
1168 return o
1168 return o
1169
1169
1170 class chunkbuffer(object):
1170 class chunkbuffer(object):
1171 """Allow arbitrary sized chunks of data to be efficiently read from an
1171 """Allow arbitrary sized chunks of data to be efficiently read from an
1172 iterator over chunks of arbitrary size."""
1172 iterator over chunks of arbitrary size."""
1173
1173
1174 def __init__(self, in_iter, targetsize = 2**16):
1174 def __init__(self, in_iter, targetsize = 2**16):
1175 """in_iter is the iterator that's iterating over the input chunks.
1175 """in_iter is the iterator that's iterating over the input chunks.
1176 targetsize is how big a buffer to try to maintain."""
1176 targetsize is how big a buffer to try to maintain."""
1177 self.in_iter = iter(in_iter)
1177 self.in_iter = iter(in_iter)
1178 self.buf = ''
1178 self.buf = ''
1179 self.targetsize = int(targetsize)
1179 self.targetsize = int(targetsize)
1180 if self.targetsize <= 0:
1180 if self.targetsize <= 0:
1181 raise ValueError(_("targetsize must be greater than 0, was %d") %
1181 raise ValueError(_("targetsize must be greater than 0, was %d") %
1182 targetsize)
1182 targetsize)
1183 self.iterempty = False
1183 self.iterempty = False
1184
1184
1185 def fillbuf(self):
1185 def fillbuf(self):
1186 """Ignore target size; read every chunk from iterator until empty."""
1186 """Ignore target size; read every chunk from iterator until empty."""
1187 if not self.iterempty:
1187 if not self.iterempty:
1188 collector = cStringIO.StringIO()
1188 collector = cStringIO.StringIO()
1189 collector.write(self.buf)
1189 collector.write(self.buf)
1190 for ch in self.in_iter:
1190 for ch in self.in_iter:
1191 collector.write(ch)
1191 collector.write(ch)
1192 self.buf = collector.getvalue()
1192 self.buf = collector.getvalue()
1193 self.iterempty = True
1193 self.iterempty = True
1194
1194
1195 def read(self, l):
1195 def read(self, l):
1196 """Read L bytes of data from the iterator of chunks of data.
1196 """Read L bytes of data from the iterator of chunks of data.
1197 Returns less than L bytes if the iterator runs dry."""
1197 Returns less than L bytes if the iterator runs dry."""
1198 if l > len(self.buf) and not self.iterempty:
1198 if l > len(self.buf) and not self.iterempty:
1199 # Clamp to a multiple of self.targetsize
1199 # Clamp to a multiple of self.targetsize
1200 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1200 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1201 collector = cStringIO.StringIO()
1201 collector = cStringIO.StringIO()
1202 collector.write(self.buf)
1202 collector.write(self.buf)
1203 collected = len(self.buf)
1203 collected = len(self.buf)
1204 for chunk in self.in_iter:
1204 for chunk in self.in_iter:
1205 collector.write(chunk)
1205 collector.write(chunk)
1206 collected += len(chunk)
1206 collected += len(chunk)
1207 if collected >= targetsize:
1207 if collected >= targetsize:
1208 break
1208 break
1209 if collected < targetsize:
1209 if collected < targetsize:
1210 self.iterempty = True
1210 self.iterempty = True
1211 self.buf = collector.getvalue()
1211 self.buf = collector.getvalue()
1212 s, self.buf = self.buf[:l], buffer(self.buf, l)
1212 s, self.buf = self.buf[:l], buffer(self.buf, l)
1213 return s
1213 return s
1214
1214
1215 def filechunkiter(f, size=65536, limit=None):
1215 def filechunkiter(f, size=65536, limit=None):
1216 """Create a generator that produces the data in the file size
1216 """Create a generator that produces the data in the file size
1217 (default 65536) bytes at a time, up to optional limit (default is
1217 (default 65536) bytes at a time, up to optional limit (default is
1218 to read all data). Chunks may be less than size bytes if the
1218 to read all data). Chunks may be less than size bytes if the
1219 chunk is the last chunk in the file, or the file is a socket or
1219 chunk is the last chunk in the file, or the file is a socket or
1220 some other type of file that sometimes reads less data than is
1220 some other type of file that sometimes reads less data than is
1221 requested."""
1221 requested."""
1222 assert size >= 0
1222 assert size >= 0
1223 assert limit is None or limit >= 0
1223 assert limit is None or limit >= 0
1224 while True:
1224 while True:
1225 if limit is None: nbytes = size
1225 if limit is None: nbytes = size
1226 else: nbytes = min(limit, size)
1226 else: nbytes = min(limit, size)
1227 s = nbytes and f.read(nbytes)
1227 s = nbytes and f.read(nbytes)
1228 if not s: break
1228 if not s: break
1229 if limit: limit -= len(s)
1229 if limit: limit -= len(s)
1230 yield s
1230 yield s
1231
1231
1232 def makedate():
1232 def makedate():
1233 lt = time.localtime()
1233 lt = time.localtime()
1234 if lt[8] == 1 and time.daylight:
1234 if lt[8] == 1 and time.daylight:
1235 tz = time.altzone
1235 tz = time.altzone
1236 else:
1236 else:
1237 tz = time.timezone
1237 tz = time.timezone
1238 return time.mktime(lt), tz
1238 return time.mktime(lt), tz
1239
1239
1240 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1240 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1241 """represent a (unixtime, offset) tuple as a localized time.
1241 """represent a (unixtime, offset) tuple as a localized time.
1242 unixtime is seconds since the epoch, and offset is the time zone's
1242 unixtime is seconds since the epoch, and offset is the time zone's
1243 number of seconds away from UTC. if timezone is false, do not
1243 number of seconds away from UTC. if timezone is false, do not
1244 append time zone to string."""
1244 append time zone to string."""
1245 t, tz = date or makedate()
1245 t, tz = date or makedate()
1246 s = time.strftime(format, time.gmtime(float(t) - tz))
1246 s = time.strftime(format, time.gmtime(float(t) - tz))
1247 if timezone:
1247 if timezone:
1248 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1248 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1249 return s
1249 return s
1250
1250
1251 def strdate(string, format, defaults):
1251 def strdate(string, format, defaults):
1252 """parse a localized time string and return a (unixtime, offset) tuple.
1252 """parse a localized time string and return a (unixtime, offset) tuple.
1253 if the string cannot be parsed, ValueError is raised."""
1253 if the string cannot be parsed, ValueError is raised."""
1254 def timezone(string):
1254 def timezone(string):
1255 tz = string.split()[-1]
1255 tz = string.split()[-1]
1256 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1256 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1257 tz = int(tz)
1257 tz = int(tz)
1258 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1258 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1259 return offset
1259 return offset
1260 if tz == "GMT" or tz == "UTC":
1260 if tz == "GMT" or tz == "UTC":
1261 return 0
1261 return 0
1262 return None
1262 return None
1263
1263
1264 # NOTE: unixtime = localunixtime + offset
1264 # NOTE: unixtime = localunixtime + offset
1265 offset, date = timezone(string), string
1265 offset, date = timezone(string), string
1266 if offset != None:
1266 if offset != None:
1267 date = " ".join(string.split()[:-1])
1267 date = " ".join(string.split()[:-1])
1268
1268
1269 # add missing elements from defaults
1269 # add missing elements from defaults
1270 for part in defaults:
1270 for part in defaults:
1271 found = [True for p in part if ("%"+p) in format]
1271 found = [True for p in part if ("%"+p) in format]
1272 if not found:
1272 if not found:
1273 date += "@" + defaults[part]
1273 date += "@" + defaults[part]
1274 format += "@%" + part[0]
1274 format += "@%" + part[0]
1275
1275
1276 timetuple = time.strptime(date, format)
1276 timetuple = time.strptime(date, format)
1277 localunixtime = int(calendar.timegm(timetuple))
1277 localunixtime = int(calendar.timegm(timetuple))
1278 if offset is None:
1278 if offset is None:
1279 # local timezone
1279 # local timezone
1280 unixtime = int(time.mktime(timetuple))
1280 unixtime = int(time.mktime(timetuple))
1281 offset = unixtime - localunixtime
1281 offset = unixtime - localunixtime
1282 else:
1282 else:
1283 unixtime = localunixtime + offset
1283 unixtime = localunixtime + offset
1284 return unixtime, offset
1284 return unixtime, offset
1285
1285
1286 def parsedate(string, formats=None, defaults=None):
1286 def parsedate(string, formats=None, defaults=None):
1287 """parse a localized time string and return a (unixtime, offset) tuple.
1287 """parse a localized time string and return a (unixtime, offset) tuple.
1288 The date may be a "unixtime offset" string or in one of the specified
1288 The date may be a "unixtime offset" string or in one of the specified
1289 formats."""
1289 formats."""
1290 if not string:
1290 if not string:
1291 return 0, 0
1291 return 0, 0
1292 if not formats:
1292 if not formats:
1293 formats = defaultdateformats
1293 formats = defaultdateformats
1294 string = string.strip()
1294 string = string.strip()
1295 try:
1295 try:
1296 when, offset = map(int, string.split(' '))
1296 when, offset = map(int, string.split(' '))
1297 except ValueError:
1297 except ValueError:
1298 # fill out defaults
1298 # fill out defaults
1299 if not defaults:
1299 if not defaults:
1300 defaults = {}
1300 defaults = {}
1301 now = makedate()
1301 now = makedate()
1302 for part in "d mb yY HI M S".split():
1302 for part in "d mb yY HI M S".split():
1303 if part not in defaults:
1303 if part not in defaults:
1304 if part[0] in "HMS":
1304 if part[0] in "HMS":
1305 defaults[part] = "00"
1305 defaults[part] = "00"
1306 elif part[0] in "dm":
1306 elif part[0] in "dm":
1307 defaults[part] = "1"
1307 defaults[part] = "1"
1308 else:
1308 else:
1309 defaults[part] = datestr(now, "%" + part[0], False)
1309 defaults[part] = datestr(now, "%" + part[0], False)
1310
1310
1311 for format in formats:
1311 for format in formats:
1312 try:
1312 try:
1313 when, offset = strdate(string, format, defaults)
1313 when, offset = strdate(string, format, defaults)
1314 except ValueError:
1314 except ValueError:
1315 pass
1315 pass
1316 else:
1316 else:
1317 break
1317 break
1318 else:
1318 else:
1319 raise Abort(_('invalid date: %r ') % string)
1319 raise Abort(_('invalid date: %r ') % string)
1320 # validate explicit (probably user-specified) date and
1320 # validate explicit (probably user-specified) date and
1321 # time zone offset. values must fit in signed 32 bits for
1321 # time zone offset. values must fit in signed 32 bits for
1322 # current 32-bit linux runtimes. timezones go from UTC-12
1322 # current 32-bit linux runtimes. timezones go from UTC-12
1323 # to UTC+14
1323 # to UTC+14
1324 if abs(when) > 0x7fffffff:
1324 if abs(when) > 0x7fffffff:
1325 raise Abort(_('date exceeds 32 bits: %d') % when)
1325 raise Abort(_('date exceeds 32 bits: %d') % when)
1326 if offset < -50400 or offset > 43200:
1326 if offset < -50400 or offset > 43200:
1327 raise Abort(_('impossible time zone offset: %d') % offset)
1327 raise Abort(_('impossible time zone offset: %d') % offset)
1328 return when, offset
1328 return when, offset
1329
1329
1330 def matchdate(date):
1330 def matchdate(date):
1331 """Return a function that matches a given date match specifier
1331 """Return a function that matches a given date match specifier
1332
1332
1333 Formats include:
1333 Formats include:
1334
1334
1335 '{date}' match a given date to the accuracy provided
1335 '{date}' match a given date to the accuracy provided
1336
1336
1337 '<{date}' on or before a given date
1337 '<{date}' on or before a given date
1338
1338
1339 '>{date}' on or after a given date
1339 '>{date}' on or after a given date
1340
1340
1341 """
1341 """
1342
1342
1343 def lower(date):
1343 def lower(date):
1344 return parsedate(date, extendeddateformats)[0]
1344 return parsedate(date, extendeddateformats)[0]
1345
1345
1346 def upper(date):
1346 def upper(date):
1347 d = dict(mb="12", HI="23", M="59", S="59")
1347 d = dict(mb="12", HI="23", M="59", S="59")
1348 for days in "31 30 29".split():
1348 for days in "31 30 29".split():
1349 try:
1349 try:
1350 d["d"] = days
1350 d["d"] = days
1351 return parsedate(date, extendeddateformats, d)[0]
1351 return parsedate(date, extendeddateformats, d)[0]
1352 except:
1352 except:
1353 pass
1353 pass
1354 d["d"] = "28"
1354 d["d"] = "28"
1355 return parsedate(date, extendeddateformats, d)[0]
1355 return parsedate(date, extendeddateformats, d)[0]
1356
1356
1357 if date[0] == "<":
1357 if date[0] == "<":
1358 when = upper(date[1:])
1358 when = upper(date[1:])
1359 return lambda x: x <= when
1359 return lambda x: x <= when
1360 elif date[0] == ">":
1360 elif date[0] == ">":
1361 when = lower(date[1:])
1361 when = lower(date[1:])
1362 return lambda x: x >= when
1362 return lambda x: x >= when
1363 elif date[0] == "-":
1363 elif date[0] == "-":
1364 try:
1364 try:
1365 days = int(date[1:])
1365 days = int(date[1:])
1366 except ValueError:
1366 except ValueError:
1367 raise Abort(_("invalid day spec: %s") % date[1:])
1367 raise Abort(_("invalid day spec: %s") % date[1:])
1368 when = makedate()[0] - days * 3600 * 24
1368 when = makedate()[0] - days * 3600 * 24
1369 return lambda x: x >= when
1369 return lambda x: x >= when
1370 elif " to " in date:
1370 elif " to " in date:
1371 a, b = date.split(" to ")
1371 a, b = date.split(" to ")
1372 start, stop = lower(a), upper(b)
1372 start, stop = lower(a), upper(b)
1373 return lambda x: x >= start and x <= stop
1373 return lambda x: x >= start and x <= stop
1374 else:
1374 else:
1375 start, stop = lower(date), upper(date)
1375 start, stop = lower(date), upper(date)
1376 return lambda x: x >= start and x <= stop
1376 return lambda x: x >= start and x <= stop
1377
1377
1378 def shortuser(user):
1378 def shortuser(user):
1379 """Return a short representation of a user name or email address."""
1379 """Return a short representation of a user name or email address."""
1380 f = user.find('@')
1380 f = user.find('@')
1381 if f >= 0:
1381 if f >= 0:
1382 user = user[:f]
1382 user = user[:f]
1383 f = user.find('<')
1383 f = user.find('<')
1384 if f >= 0:
1384 if f >= 0:
1385 user = user[f+1:]
1385 user = user[f+1:]
1386 f = user.find(' ')
1386 f = user.find(' ')
1387 if f >= 0:
1387 if f >= 0:
1388 user = user[:f]
1388 user = user[:f]
1389 f = user.find('.')
1389 f = user.find('.')
1390 if f >= 0:
1390 if f >= 0:
1391 user = user[:f]
1391 user = user[:f]
1392 return user
1392 return user
1393
1393
1394 def ellipsis(text, maxlength=400):
1394 def ellipsis(text, maxlength=400):
1395 """Trim string to at most maxlength (default: 400) characters."""
1395 """Trim string to at most maxlength (default: 400) characters."""
1396 if len(text) <= maxlength:
1396 if len(text) <= maxlength:
1397 return text
1397 return text
1398 else:
1398 else:
1399 return "%s..." % (text[:maxlength-3])
1399 return "%s..." % (text[:maxlength-3])
1400
1400
1401 def walkrepos(path):
1401 def walkrepos(path):
1402 '''yield every hg repository under path, recursively.'''
1402 '''yield every hg repository under path, recursively.'''
1403 def errhandler(err):
1403 def errhandler(err):
1404 if err.filename == path:
1404 if err.filename == path:
1405 raise err
1405 raise err
1406
1406
1407 for root, dirs, files in os.walk(path, onerror=errhandler):
1407 for root, dirs, files in os.walk(path, onerror=errhandler):
1408 for d in dirs:
1408 for d in dirs:
1409 if d == '.hg':
1409 if d == '.hg':
1410 yield root
1410 yield root
1411 dirs[:] = []
1411 dirs[:] = []
1412 break
1412 break
1413
1413
1414 _rcpath = None
1414 _rcpath = None
1415
1415
1416 def os_rcpath():
1416 def os_rcpath():
1417 '''return default os-specific hgrc search path'''
1417 '''return default os-specific hgrc search path'''
1418 path = system_rcpath()
1418 path = system_rcpath()
1419 path.extend(user_rcpath())
1419 path.extend(user_rcpath())
1420 path = [os.path.normpath(f) for f in path]
1420 path = [os.path.normpath(f) for f in path]
1421 return path
1421 return path
1422
1422
1423 def rcpath():
1423 def rcpath():
1424 '''return hgrc search path. if env var HGRCPATH is set, use it.
1424 '''return hgrc search path. if env var HGRCPATH is set, use it.
1425 for each item in path, if directory, use files ending in .rc,
1425 for each item in path, if directory, use files ending in .rc,
1426 else use item.
1426 else use item.
1427 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1427 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1428 if no HGRCPATH, use default os-specific path.'''
1428 if no HGRCPATH, use default os-specific path.'''
1429 global _rcpath
1429 global _rcpath
1430 if _rcpath is None:
1430 if _rcpath is None:
1431 if 'HGRCPATH' in os.environ:
1431 if 'HGRCPATH' in os.environ:
1432 _rcpath = []
1432 _rcpath = []
1433 for p in os.environ['HGRCPATH'].split(os.pathsep):
1433 for p in os.environ['HGRCPATH'].split(os.pathsep):
1434 if not p: continue
1434 if not p: continue
1435 if os.path.isdir(p):
1435 if os.path.isdir(p):
1436 for f in os.listdir(p):
1436 for f in os.listdir(p):
1437 if f.endswith('.rc'):
1437 if f.endswith('.rc'):
1438 _rcpath.append(os.path.join(p, f))
1438 _rcpath.append(os.path.join(p, f))
1439 else:
1439 else:
1440 _rcpath.append(p)
1440 _rcpath.append(p)
1441 else:
1441 else:
1442 _rcpath = os_rcpath()
1442 _rcpath = os_rcpath()
1443 return _rcpath
1443 return _rcpath
1444
1444
1445 def bytecount(nbytes):
1445 def bytecount(nbytes):
1446 '''return byte count formatted as readable string, with units'''
1446 '''return byte count formatted as readable string, with units'''
1447
1447
1448 units = (
1448 units = (
1449 (100, 1<<30, _('%.0f GB')),
1449 (100, 1<<30, _('%.0f GB')),
1450 (10, 1<<30, _('%.1f GB')),
1450 (10, 1<<30, _('%.1f GB')),
1451 (1, 1<<30, _('%.2f GB')),
1451 (1, 1<<30, _('%.2f GB')),
1452 (100, 1<<20, _('%.0f MB')),
1452 (100, 1<<20, _('%.0f MB')),
1453 (10, 1<<20, _('%.1f MB')),
1453 (10, 1<<20, _('%.1f MB')),
1454 (1, 1<<20, _('%.2f MB')),
1454 (1, 1<<20, _('%.2f MB')),
1455 (100, 1<<10, _('%.0f KB')),
1455 (100, 1<<10, _('%.0f KB')),
1456 (10, 1<<10, _('%.1f KB')),
1456 (10, 1<<10, _('%.1f KB')),
1457 (1, 1<<10, _('%.2f KB')),
1457 (1, 1<<10, _('%.2f KB')),
1458 (1, 1, _('%.0f bytes')),
1458 (1, 1, _('%.0f bytes')),
1459 )
1459 )
1460
1460
1461 for multiplier, divisor, format in units:
1461 for multiplier, divisor, format in units:
1462 if nbytes >= divisor * multiplier:
1462 if nbytes >= divisor * multiplier:
1463 return format % (nbytes / float(divisor))
1463 return format % (nbytes / float(divisor))
1464 return units[-1][2] % nbytes
1464 return units[-1][2] % nbytes
1465
1465
1466 def drop_scheme(scheme, path):
1466 def drop_scheme(scheme, path):
1467 sc = scheme + ':'
1467 sc = scheme + ':'
1468 if path.startswith(sc):
1468 if path.startswith(sc):
1469 path = path[len(sc):]
1469 path = path[len(sc):]
1470 if path.startswith('//'):
1470 if path.startswith('//'):
1471 path = path[2:]
1471 path = path[2:]
1472 return path
1472 return path
@@ -1,74 +1,91 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 cleanpath()
3 cleanpath()
4 {
4 {
5 sed -e "s:/.*\(/test/.*\):...\1:"
5 sed -e "s:/.*\(/test/.*\):...\1:"
6 }
6 }
7
7
8 echo % commit date test
8 echo % commit date test
9 hg init test
9 hg init test
10 cd test
10 cd test
11 echo foo > foo
11 echo foo > foo
12 hg add foo
12 hg add foo
13 hg commit -d '0 0' -m commit-1
13 hg commit -d '0 0' -m commit-1
14 echo foo >> foo
14 echo foo >> foo
15 hg commit -d '1 4444444' -m commit-3
15 hg commit -d '1 4444444' -m commit-3
16 hg commit -d '1 15.1' -m commit-4
16 hg commit -d '1 15.1' -m commit-4
17 hg commit -d 'foo bar' -m commit-5
17 hg commit -d 'foo bar' -m commit-5
18 hg commit -d ' 1 4444' -m commit-6
18 hg commit -d ' 1 4444' -m commit-6
19 hg commit -d '111111111111 0' -m commit-7
19 hg commit -d '111111111111 0' -m commit-7
20
20
21 echo % partial commit test
21 echo % partial commit test
22 echo bar > bar
22 echo bar > bar
23 hg add bar
23 hg add bar
24 rm bar
24 rm bar
25 hg commit -d "1000000 0" -m commit-8 2>&1 | cleanpath
25 hg commit -d "1000000 0" -m commit-8 2>&1 | cleanpath
26
26
27 hg -q revert -a --no-backup
27 hg -q revert -a --no-backup
28
28
29 mkdir dir
29 mkdir dir
30 echo boo > dir/file
30 echo boo > dir/file
31 hg add
31 hg add
32 hg -v commit -d '0 0' -m commit-9 dir
32 hg -v commit -d '0 0' -m commit-9 dir
33
33
34 echo > dir.file
34 echo > dir.file
35 hg add
35 hg add
36 hg commit -d '0 0' -m commit-10 dir dir.file 2>&1 | cleanpath
36 hg commit -d '0 0' -m commit-10 dir dir.file 2>&1 | cleanpath
37
37
38 echo >> dir/file
38 echo >> dir/file
39 mkdir bleh
39 mkdir bleh
40 mkdir dir2
40 mkdir dir2
41 cd bleh
41 cd bleh
42 hg commit -d '0 0' -m commit-11 . 2>&1 | cleanpath
42 hg commit -d '0 0' -m commit-11 . 2>&1 | cleanpath
43 hg commit -d '0 0' -m commit-12 ../dir ../dir2 2>&1 | cleanpath
43 hg commit -d '0 0' -m commit-12 ../dir ../dir2 2>&1 | cleanpath
44 hg -v commit -d '0 0' -m commit-13 ../dir
44 hg -v commit -d '0 0' -m commit-13 ../dir
45 cd ..
45 cd ..
46
46
47 hg commit -d '0 0' -m commit-14 does-not-exist 2>&1 | cleanpath
47 hg commit -d '0 0' -m commit-14 does-not-exist 2>&1 | cleanpath
48 ln -s foo baz
48 ln -s foo baz
49 hg commit -d '0 0' -m commit-15 baz 2>&1 | cleanpath
49 hg commit -d '0 0' -m commit-15 baz 2>&1 | cleanpath
50 touch quux
50 touch quux
51 hg commit -d '0 0' -m commit-16 quux 2>&1 | cleanpath
51 hg commit -d '0 0' -m commit-16 quux 2>&1 | cleanpath
52 echo >> dir/file
52 echo >> dir/file
53 hg -v commit -d '0 0' -m commit-17 dir/file
53 hg -v commit -d '0 0' -m commit-17 dir/file
54 cd ..
54 cd ..
55
55
56 echo % partial subdir commit test
56 echo % partial subdir commit test
57 hg init test2
57 hg init test2
58 cd test2
58 cd test2
59 mkdir foo
59 mkdir foo
60 echo foo > foo/foo
60 echo foo > foo/foo
61 mkdir bar
61 mkdir bar
62 echo bar > bar/bar
62 echo bar > bar/bar
63 hg add
63 hg add
64 hg ci -d '1000000 0' -u test -m commit-subdir-1 foo
64 hg ci -d '1000000 0' -u test -m commit-subdir-1 foo
65 hg ci -d '1000001 0' -u test -m commit-subdir-2 bar
65 hg ci -d '1000001 0' -u test -m commit-subdir-2 bar
66 echo % subdir log 1
66 echo % subdir log 1
67 hg log -v foo
67 hg log -v foo
68 echo % subdir log 2
68 echo % subdir log 2
69 hg log -v bar
69 hg log -v bar
70 echo % full log
70 echo % full log
71 hg log -v
71 hg log -v
72 cd ..
72 cd ..
73
73
74 echo % dot and subdir commit test
75 hg init test3
76 cd test3
77 mkdir foo
78 echo foo content > foo/plain-file
79 hg add foo/plain-file
80 hg ci -d '1000000 0' -u test -m commit-foo-subdir foo
81 echo modified foo content > foo/plain-file
82 hg ci -d '2000000 0' -u test -m commit-foo-dot .
83 echo % full log
84 hg log -v
85 echo % subdir log
86 cd foo
87 hg log .
88 cd ..
89 cd ..
90
74 exit 0
91 exit 0
@@ -1,66 +1,97 b''
1 % commit date test
1 % commit date test
2 abort: impossible time zone offset: 4444444
2 abort: impossible time zone offset: 4444444
3 transaction abort!
3 transaction abort!
4 rollback completed
4 rollback completed
5 abort: invalid date: '1\t15.1'
5 abort: invalid date: '1\t15.1'
6 transaction abort!
6 transaction abort!
7 rollback completed
7 rollback completed
8 abort: invalid date: 'foo bar'
8 abort: invalid date: 'foo bar'
9 transaction abort!
9 transaction abort!
10 rollback completed
10 rollback completed
11 nothing changed
11 nothing changed
12 % partial commit test
12 % partial commit test
13 trouble committing bar!
13 trouble committing bar!
14 abort: No such file or directory: .../test/bar
14 abort: No such file or directory: .../test/bar
15 adding dir/file
15 adding dir/file
16 dir/file
16 dir/file
17 adding dir.file
17 adding dir.file
18 abort: no match under directory .../test/dir!
18 abort: no match under directory .../test/dir!
19 abort: no match under directory .../test/bleh!
19 abort: no match under directory .../test/bleh!
20 abort: no match under directory .../test/dir2!
20 abort: no match under directory .../test/dir2!
21 dir/file
21 dir/file
22 does-not-exist: No such file or directory
22 does-not-exist: No such file or directory
23 abort: file .../test/does-not-exist not found!
23 abort: file .../test/does-not-exist not found!
24 abort: file .../test/baz not tracked!
24 abort: file .../test/baz not tracked!
25 abort: file .../test/quux not tracked!
25 abort: file .../test/quux not tracked!
26 dir/file
26 dir/file
27 % partial subdir commit test
27 % partial subdir commit test
28 adding bar/bar
28 adding bar/bar
29 adding foo/foo
29 adding foo/foo
30 % subdir log 1
30 % subdir log 1
31 changeset: 0:6ef3cb06bb80
31 changeset: 0:6ef3cb06bb80
32 user: test
32 user: test
33 date: Mon Jan 12 13:46:40 1970 +0000
33 date: Mon Jan 12 13:46:40 1970 +0000
34 files: foo/foo
34 files: foo/foo
35 description:
35 description:
36 commit-subdir-1
36 commit-subdir-1
37
37
38
38
39 % subdir log 2
39 % subdir log 2
40 changeset: 1:f2e51572cf5a
40 changeset: 1:f2e51572cf5a
41 tag: tip
41 tag: tip
42 user: test
42 user: test
43 date: Mon Jan 12 13:46:41 1970 +0000
43 date: Mon Jan 12 13:46:41 1970 +0000
44 files: bar/bar
44 files: bar/bar
45 description:
45 description:
46 commit-subdir-2
46 commit-subdir-2
47
47
48
48
49 % full log
49 % full log
50 changeset: 1:f2e51572cf5a
50 changeset: 1:f2e51572cf5a
51 tag: tip
51 tag: tip
52 user: test
52 user: test
53 date: Mon Jan 12 13:46:41 1970 +0000
53 date: Mon Jan 12 13:46:41 1970 +0000
54 files: bar/bar
54 files: bar/bar
55 description:
55 description:
56 commit-subdir-2
56 commit-subdir-2
57
57
58
58
59 changeset: 0:6ef3cb06bb80
59 changeset: 0:6ef3cb06bb80
60 user: test
60 user: test
61 date: Mon Jan 12 13:46:40 1970 +0000
61 date: Mon Jan 12 13:46:40 1970 +0000
62 files: foo/foo
62 files: foo/foo
63 description:
63 description:
64 commit-subdir-1
64 commit-subdir-1
65
65
66
66
67 % dot and subdir commit test
68 % full log
69 changeset: 1:d9180e04fa8a
70 tag: tip
71 user: test
72 date: Sat Jan 24 03:33:20 1970 +0000
73 files: foo/plain-file
74 description:
75 commit-foo-dot
76
77
78 changeset: 0:80b572aaf098
79 user: test
80 date: Mon Jan 12 13:46:40 1970 +0000
81 files: foo/plain-file
82 description:
83 commit-foo-subdir
84
85
86 % subdir log
87 changeset: 1:d9180e04fa8a
88 tag: tip
89 user: test
90 date: Sat Jan 24 03:33:20 1970 +0000
91 summary: commit-foo-dot
92
93 changeset: 0:80b572aaf098
94 user: test
95 date: Mon Jan 12 13:46:40 1970 +0000
96 summary: commit-foo-subdir
97
@@ -1,291 +1,301 b''
1 adding beans/black
1 adding beans/black
2 adding beans/borlotti
2 adding beans/borlotti
3 adding beans/kidney
3 adding beans/kidney
4 adding beans/navy
4 adding beans/navy
5 adding beans/pinto
5 adding beans/pinto
6 adding beans/turtle
6 adding beans/turtle
7 adding fennel
7 adding fennel
8 adding fenugreek
8 adding fenugreek
9 adding fiddlehead
9 adding fiddlehead
10 adding glob:glob
10 adding glob:glob
11 adding mammals/Procyonidae/cacomistle
11 adding mammals/Procyonidae/cacomistle
12 adding mammals/Procyonidae/coatimundi
12 adding mammals/Procyonidae/coatimundi
13 adding mammals/Procyonidae/raccoon
13 adding mammals/Procyonidae/raccoon
14 adding mammals/skunk
14 adding mammals/skunk
15 hg debugwalk
15 hg debugwalk
16 f beans/black beans/black
16 f beans/black beans/black
17 f beans/borlotti beans/borlotti
17 f beans/borlotti beans/borlotti
18 f beans/kidney beans/kidney
18 f beans/kidney beans/kidney
19 f beans/navy beans/navy
19 f beans/navy beans/navy
20 f beans/pinto beans/pinto
20 f beans/pinto beans/pinto
21 f beans/turtle beans/turtle
21 f beans/turtle beans/turtle
22 f fennel fennel
22 f fennel fennel
23 f fenugreek fenugreek
23 f fenugreek fenugreek
24 f fiddlehead fiddlehead
24 f fiddlehead fiddlehead
25 f glob:glob glob:glob
25 f glob:glob glob:glob
26 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
26 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
27 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
27 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
28 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
28 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
29 f mammals/skunk mammals/skunk
29 f mammals/skunk mammals/skunk
30
30
31 hg debugwalk -I.
31 hg debugwalk -I.
32 f beans/black beans/black
32 f beans/black beans/black
33 f beans/borlotti beans/borlotti
33 f beans/borlotti beans/borlotti
34 f beans/kidney beans/kidney
34 f beans/kidney beans/kidney
35 f beans/navy beans/navy
35 f beans/navy beans/navy
36 f beans/pinto beans/pinto
36 f beans/pinto beans/pinto
37 f beans/turtle beans/turtle
37 f beans/turtle beans/turtle
38 f fennel fennel
38 f fennel fennel
39 f fenugreek fenugreek
39 f fenugreek fenugreek
40 f fiddlehead fiddlehead
40 f fiddlehead fiddlehead
41 f glob:glob glob:glob
41 f glob:glob glob:glob
42 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
42 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
43 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
43 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
44 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
44 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
45 f mammals/skunk mammals/skunk
45 f mammals/skunk mammals/skunk
46
46
47 cd mammals
47 cd mammals
48
48
49 hg debugwalk
49 hg debugwalk
50 f beans/black ../beans/black
50 f beans/black ../beans/black
51 f beans/borlotti ../beans/borlotti
51 f beans/borlotti ../beans/borlotti
52 f beans/kidney ../beans/kidney
52 f beans/kidney ../beans/kidney
53 f beans/navy ../beans/navy
53 f beans/navy ../beans/navy
54 f beans/pinto ../beans/pinto
54 f beans/pinto ../beans/pinto
55 f beans/turtle ../beans/turtle
55 f beans/turtle ../beans/turtle
56 f fennel ../fennel
56 f fennel ../fennel
57 f fenugreek ../fenugreek
57 f fenugreek ../fenugreek
58 f fiddlehead ../fiddlehead
58 f fiddlehead ../fiddlehead
59 f glob:glob ../glob:glob
59 f glob:glob ../glob:glob
60 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
60 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
61 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
61 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
62 f mammals/Procyonidae/raccoon Procyonidae/raccoon
62 f mammals/Procyonidae/raccoon Procyonidae/raccoon
63 f mammals/skunk skunk
63 f mammals/skunk skunk
64
64
65 hg debugwalk -X ../beans
65 hg debugwalk -X ../beans
66 f fennel ../fennel
66 f fennel ../fennel
67 f fenugreek ../fenugreek
67 f fenugreek ../fenugreek
68 f fiddlehead ../fiddlehead
68 f fiddlehead ../fiddlehead
69 f glob:glob ../glob:glob
69 f glob:glob ../glob:glob
70 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
70 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
71 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
71 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
72 f mammals/Procyonidae/raccoon Procyonidae/raccoon
72 f mammals/Procyonidae/raccoon Procyonidae/raccoon
73 f mammals/skunk skunk
73 f mammals/skunk skunk
74
74
75 hg debugwalk -I *k
75 hg debugwalk -I *k
76 f mammals/skunk skunk
76 f mammals/skunk skunk
77
77
78 hg debugwalk -I glob:*k
78 hg debugwalk -I glob:*k
79 f mammals/skunk skunk
79 f mammals/skunk skunk
80
80
81 hg debugwalk -I relglob:*k
81 hg debugwalk -I relglob:*k
82 f beans/black ../beans/black
82 f beans/black ../beans/black
83 f fenugreek ../fenugreek
83 f fenugreek ../fenugreek
84 f mammals/skunk skunk
84 f mammals/skunk skunk
85
85
86 hg debugwalk -I relglob:*k .
86 hg debugwalk -I relglob:*k .
87 f mammals/skunk skunk
87 f mammals/skunk skunk
88
88
89 hg debugwalk -I re:.*k$
89 hg debugwalk -I re:.*k$
90 f beans/black ../beans/black
90 f beans/black ../beans/black
91 f fenugreek ../fenugreek
91 f fenugreek ../fenugreek
92 f mammals/skunk skunk
92 f mammals/skunk skunk
93
93
94 hg debugwalk -I relre:.*k$
94 hg debugwalk -I relre:.*k$
95 f beans/black ../beans/black
95 f beans/black ../beans/black
96 f fenugreek ../fenugreek
96 f fenugreek ../fenugreek
97 f mammals/skunk skunk
97 f mammals/skunk skunk
98
98
99 hg debugwalk -I path:beans
99 hg debugwalk -I path:beans
100 f beans/black ../beans/black
100 f beans/black ../beans/black
101 f beans/borlotti ../beans/borlotti
101 f beans/borlotti ../beans/borlotti
102 f beans/kidney ../beans/kidney
102 f beans/kidney ../beans/kidney
103 f beans/navy ../beans/navy
103 f beans/navy ../beans/navy
104 f beans/pinto ../beans/pinto
104 f beans/pinto ../beans/pinto
105 f beans/turtle ../beans/turtle
105 f beans/turtle ../beans/turtle
106
106
107 hg debugwalk -I relpath:../beans
107 hg debugwalk -I relpath:../beans
108 f beans/black ../beans/black
108 f beans/black ../beans/black
109 f beans/borlotti ../beans/borlotti
109 f beans/borlotti ../beans/borlotti
110 f beans/kidney ../beans/kidney
110 f beans/kidney ../beans/kidney
111 f beans/navy ../beans/navy
111 f beans/navy ../beans/navy
112 f beans/pinto ../beans/pinto
112 f beans/pinto ../beans/pinto
113 f beans/turtle ../beans/turtle
113 f beans/turtle ../beans/turtle
114
114
115 hg debugwalk .
115 hg debugwalk .
116 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
116 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
117 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
117 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
118 f mammals/Procyonidae/raccoon Procyonidae/raccoon
118 f mammals/Procyonidae/raccoon Procyonidae/raccoon
119 f mammals/skunk skunk
119 f mammals/skunk skunk
120
120
121 hg debugwalk -I.
121 hg debugwalk -I.
122 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
122 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
123 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
123 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
124 f mammals/Procyonidae/raccoon Procyonidae/raccoon
124 f mammals/Procyonidae/raccoon Procyonidae/raccoon
125 f mammals/skunk skunk
125 f mammals/skunk skunk
126
126
127 hg debugwalk Procyonidae
127 hg debugwalk Procyonidae
128 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
128 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
129 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
129 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
130 f mammals/Procyonidae/raccoon Procyonidae/raccoon
130 f mammals/Procyonidae/raccoon Procyonidae/raccoon
131
131
132 cd Procyonidae
132 cd Procyonidae
133
133
134 hg debugwalk .
134 hg debugwalk .
135 f mammals/Procyonidae/cacomistle cacomistle
135 f mammals/Procyonidae/cacomistle cacomistle
136 f mammals/Procyonidae/coatimundi coatimundi
136 f mammals/Procyonidae/coatimundi coatimundi
137 f mammals/Procyonidae/raccoon raccoon
137 f mammals/Procyonidae/raccoon raccoon
138
138
139 hg debugwalk ..
139 hg debugwalk ..
140 f mammals/Procyonidae/cacomistle cacomistle
140 f mammals/Procyonidae/cacomistle cacomistle
141 f mammals/Procyonidae/coatimundi coatimundi
141 f mammals/Procyonidae/coatimundi coatimundi
142 f mammals/Procyonidae/raccoon raccoon
142 f mammals/Procyonidae/raccoon raccoon
143 f mammals/skunk ../skunk
143 f mammals/skunk ../skunk
144
144
145 cd ..
145 cd ..
146
146
147 hg debugwalk ../beans
147 hg debugwalk ../beans
148 f beans/black ../beans/black
148 f beans/black ../beans/black
149 f beans/borlotti ../beans/borlotti
149 f beans/borlotti ../beans/borlotti
150 f beans/kidney ../beans/kidney
150 f beans/kidney ../beans/kidney
151 f beans/navy ../beans/navy
151 f beans/navy ../beans/navy
152 f beans/pinto ../beans/pinto
152 f beans/pinto ../beans/pinto
153 f beans/turtle ../beans/turtle
153 f beans/turtle ../beans/turtle
154
154
155 hg debugwalk .
155 hg debugwalk .
156 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
156 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
157 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
157 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
158 f mammals/Procyonidae/raccoon Procyonidae/raccoon
158 f mammals/Procyonidae/raccoon Procyonidae/raccoon
159 f mammals/skunk skunk
159 f mammals/skunk skunk
160
160
161 hg debugwalk .hg
161 hg debugwalk .hg
162 .hg: No such file or directory
162 .hg: No such file or directory
163
163
164 hg debugwalk ../.hg
164 hg debugwalk ../.hg
165 abort: path contains illegal component: .hg
165 abort: path contains illegal component: .hg
166
166
167
167
168 cd ..
168 cd ..
169
169
170 hg debugwalk -Ibeans
170 hg debugwalk -Ibeans
171 f beans/black beans/black
171 f beans/black beans/black
172 f beans/borlotti beans/borlotti
172 f beans/borlotti beans/borlotti
173 f beans/kidney beans/kidney
173 f beans/kidney beans/kidney
174 f beans/navy beans/navy
174 f beans/navy beans/navy
175 f beans/pinto beans/pinto
175 f beans/pinto beans/pinto
176 f beans/turtle beans/turtle
176 f beans/turtle beans/turtle
177
177
178 hg debugwalk glob:mammals/../beans/b*
178 hg debugwalk glob:mammals/../beans/b*
179 f beans/black beans/black
179 f beans/black beans/black
180 f beans/borlotti beans/borlotti
180 f beans/borlotti beans/borlotti
181
181
182 hg debugwalk -X*/Procyonidae mammals
182 hg debugwalk -X*/Procyonidae mammals
183 f mammals/skunk mammals/skunk
183 f mammals/skunk mammals/skunk
184
184
185 hg debugwalk path:mammals
185 hg debugwalk path:mammals
186 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
186 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
187 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
187 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
188 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
188 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
189 f mammals/skunk mammals/skunk
189 f mammals/skunk mammals/skunk
190
190
191 hg debugwalk ..
191 hg debugwalk ..
192 abort: .. not under root
192 abort: .. not under root
193
193
194 hg debugwalk beans/../..
194 hg debugwalk beans/../..
195 abort: beans/../.. not under root
195 abort: beans/../.. not under root
196
196
197 hg debugwalk .hg
197 hg debugwalk .hg
198 abort: path contains illegal component: .hg
198 abort: path contains illegal component: .hg
199
199
200
200
201 hg debugwalk beans/../.hg
201 hg debugwalk beans/../.hg
202 abort: path contains illegal component: .hg
202 abort: path contains illegal component: .hg
203
203
204
204
205 hg debugwalk beans/../.hg/data
205 hg debugwalk beans/../.hg/data
206 abort: path contains illegal component: .hg/data
206 abort: path contains illegal component: .hg/data
207
207
208
208
209 hg debugwalk beans/.hg
209 hg debugwalk beans/.hg
210 beans/.hg: No such file or directory
210 beans/.hg: No such file or directory
211
211
212 hg debugwalk glob:*
212 hg debugwalk glob:*
213 f fennel fennel
213 f beans/black beans/black
214 f fenugreek fenugreek
214 f beans/borlotti beans/borlotti
215 f fiddlehead fiddlehead
215 f beans/kidney beans/kidney
216 f glob:glob glob:glob
216 f beans/navy beans/navy
217 f beans/pinto beans/pinto
218 f beans/turtle beans/turtle
219 f fennel fennel
220 f fenugreek fenugreek
221 f fiddlehead fiddlehead
222 f glob:glob glob:glob
223 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
224 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
225 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
226 f mammals/skunk mammals/skunk
217
227
218 hg debugwalk re:.*[kb]$
228 hg debugwalk re:.*[kb]$
219 f beans/black beans/black
229 f beans/black beans/black
220 f fenugreek fenugreek
230 f fenugreek fenugreek
221 f glob:glob glob:glob
231 f glob:glob glob:glob
222 f mammals/skunk mammals/skunk
232 f mammals/skunk mammals/skunk
223
233
224 hg debugwalk path:beans/black
234 hg debugwalk path:beans/black
225 f beans/black beans/black exact
235 f beans/black beans/black exact
226
236
227 hg debugwalk path:beans//black
237 hg debugwalk path:beans//black
228 f beans/black beans/black exact
238 f beans/black beans/black exact
229
239
230 hg debugwalk relglob:Procyonidae
240 hg debugwalk relglob:Procyonidae
231 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
241 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
232 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
242 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
233 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
243 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
234
244
235 hg debugwalk relglob:Procyonidae/ fennel
245 hg debugwalk relglob:Procyonidae/ fennel
236 f fennel fennel exact
246 f fennel fennel exact
237 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
247 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
238 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
248 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
239 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
249 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
240
250
241 hg debugwalk beans glob:beans/*
251 hg debugwalk beans glob:beans/*
242 f beans/black beans/black
252 f beans/black beans/black
243 f beans/borlotti beans/borlotti
253 f beans/borlotti beans/borlotti
244 f beans/kidney beans/kidney
254 f beans/kidney beans/kidney
245 f beans/navy beans/navy
255 f beans/navy beans/navy
246 f beans/pinto beans/pinto
256 f beans/pinto beans/pinto
247 f beans/turtle beans/turtle
257 f beans/turtle beans/turtle
248
258
249 hg debugwalk glob:mamm**
259 hg debugwalk glob:mamm**
250 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
260 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
251 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
261 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
252 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
262 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
253 f mammals/skunk mammals/skunk
263 f mammals/skunk mammals/skunk
254
264
255 hg debugwalk glob:mamm** fennel
265 hg debugwalk glob:mamm** fennel
256 f fennel fennel exact
266 f fennel fennel exact
257 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
267 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
258 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
268 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
259 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
269 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
260 f mammals/skunk mammals/skunk
270 f mammals/skunk mammals/skunk
261
271
262 hg debugwalk glob:j*
272 hg debugwalk glob:j*
263
273
264 hg debugwalk NOEXIST
274 hg debugwalk NOEXIST
265 NOEXIST: No such file or directory
275 NOEXIST: No such file or directory
266
276
267 hg debugwalk fifo
277 hg debugwalk fifo
268 fifo: unsupported file type (type is fifo)
278 fifo: unsupported file type (type is fifo)
269
279
270 hg debugwalk fenugreek
280 hg debugwalk fenugreek
271 m fenugreek fenugreek exact
281 m fenugreek fenugreek exact
272
282
273 hg debugwalk fenugreek
283 hg debugwalk fenugreek
274 m fenugreek fenugreek exact
284 m fenugreek fenugreek exact
275
285
276 hg debugwalk new
286 hg debugwalk new
277 f new new exact
287 f new new exact
278
288
279 cd ..
289 cd ..
280
290
281 hg debugwalk -R t t/mammals/skunk
291 hg debugwalk -R t t/mammals/skunk
282 f mammals/skunk t/mammals/skunk exact
292 f mammals/skunk t/mammals/skunk exact
283
293
284 cd t2
294 cd t2
285
295
286 hg debugwalk -R ../t ../t/mammals/skunk
296 hg debugwalk -R ../t ../t/mammals/skunk
287 f mammals/skunk ../t/mammals/skunk exact
297 f mammals/skunk ../t/mammals/skunk exact
288
298
289 hg debugwalk --cwd ../t mammals/skunk
299 hg debugwalk --cwd ../t mammals/skunk
290 f mammals/skunk mammals/skunk exact
300 f mammals/skunk mammals/skunk exact
291
301
General Comments 0
You need to be logged in to leave comments. Login now