##// END OF EJS Templates
Introduce find_exe. Use instead of find_in_path for programs....
Bryan O'Sullivan -
r4488:62019c44 default
parent child Browse files
Show More
@@ -1,3377 +1,3372 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 == '.':
433 if f == '.':
434 continue
434 continue
435 if f not in files:
435 if f not in files:
436 rf = repo.wjoin(f)
436 rf = repo.wjoin(f)
437 if f in unknown:
437 if f in unknown:
438 raise util.Abort(_("file %s not tracked!") % rf)
438 raise util.Abort(_("file %s not tracked!") % rf)
439 try:
439 try:
440 mode = os.lstat(rf)[stat.ST_MODE]
440 mode = os.lstat(rf)[stat.ST_MODE]
441 except OSError:
441 except OSError:
442 raise util.Abort(_("file %s not found!") % rf)
442 raise util.Abort(_("file %s not found!") % rf)
443 if stat.S_ISDIR(mode):
443 if stat.S_ISDIR(mode):
444 name = f + '/'
444 name = f + '/'
445 if slist is None:
445 if slist is None:
446 slist = list(files)
446 slist = list(files)
447 slist.sort()
447 slist.sort()
448 i = bisect.bisect(slist, name)
448 i = bisect.bisect(slist, name)
449 if i >= len(slist) or not slist[i].startswith(name):
449 if i >= len(slist) or not slist[i].startswith(name):
450 raise util.Abort(_("no match under directory %s!")
450 raise util.Abort(_("no match under directory %s!")
451 % rf)
451 % rf)
452 elif not stat.S_ISREG(mode):
452 elif not stat.S_ISREG(mode):
453 raise util.Abort(_("can't commit %s: "
453 raise util.Abort(_("can't commit %s: "
454 "unsupported file type!") % rf)
454 "unsupported file type!") % rf)
455 else:
455 else:
456 files = []
456 files = []
457 try:
457 try:
458 repo.commit(files, message, opts['user'], opts['date'], match,
458 repo.commit(files, message, opts['user'], opts['date'], match,
459 force_editor=opts.get('force_editor'))
459 force_editor=opts.get('force_editor'))
460 except ValueError, inst:
460 except ValueError, inst:
461 raise util.Abort(str(inst))
461 raise util.Abort(str(inst))
462
462
463 def docopy(ui, repo, pats, opts, wlock):
463 def docopy(ui, repo, pats, opts, wlock):
464 # called with the repo lock held
464 # called with the repo lock held
465 #
465 #
466 # hgsep => pathname that uses "/" to separate directories
466 # hgsep => pathname that uses "/" to separate directories
467 # ossep => pathname that uses os.sep to separate directories
467 # ossep => pathname that uses os.sep to separate directories
468 cwd = repo.getcwd()
468 cwd = repo.getcwd()
469 errors = 0
469 errors = 0
470 copied = []
470 copied = []
471 targets = {}
471 targets = {}
472
472
473 # abs: hgsep
473 # abs: hgsep
474 # rel: ossep
474 # rel: ossep
475 # return: hgsep
475 # return: hgsep
476 def okaytocopy(abs, rel, exact):
476 def okaytocopy(abs, rel, exact):
477 reasons = {'?': _('is not managed'),
477 reasons = {'?': _('is not managed'),
478 'a': _('has been marked for add'),
478 'a': _('has been marked for add'),
479 'r': _('has been marked for remove')}
479 'r': _('has been marked for remove')}
480 state = repo.dirstate.state(abs)
480 state = repo.dirstate.state(abs)
481 reason = reasons.get(state)
481 reason = reasons.get(state)
482 if reason:
482 if reason:
483 if state == 'a':
483 if state == 'a':
484 origsrc = repo.dirstate.copied(abs)
484 origsrc = repo.dirstate.copied(abs)
485 if origsrc is not None:
485 if origsrc is not None:
486 return origsrc
486 return origsrc
487 if exact:
487 if exact:
488 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
488 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
489 else:
489 else:
490 return abs
490 return abs
491
491
492 # origsrc: hgsep
492 # origsrc: hgsep
493 # abssrc: hgsep
493 # abssrc: hgsep
494 # relsrc: ossep
494 # relsrc: ossep
495 # target: ossep
495 # target: ossep
496 def copy(origsrc, abssrc, relsrc, target, exact):
496 def copy(origsrc, abssrc, relsrc, target, exact):
497 abstarget = util.canonpath(repo.root, cwd, target)
497 abstarget = util.canonpath(repo.root, cwd, target)
498 reltarget = util.pathto(repo.root, cwd, abstarget)
498 reltarget = util.pathto(repo.root, cwd, abstarget)
499 prevsrc = targets.get(abstarget)
499 prevsrc = targets.get(abstarget)
500 if prevsrc is not None:
500 if prevsrc is not None:
501 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
501 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
502 (reltarget, util.localpath(abssrc),
502 (reltarget, util.localpath(abssrc),
503 util.localpath(prevsrc)))
503 util.localpath(prevsrc)))
504 return
504 return
505 if (not opts['after'] and os.path.exists(reltarget) or
505 if (not opts['after'] and os.path.exists(reltarget) or
506 opts['after'] and repo.dirstate.state(abstarget) not in '?ar'):
506 opts['after'] and repo.dirstate.state(abstarget) not in '?ar'):
507 if not opts['force']:
507 if not opts['force']:
508 ui.warn(_('%s: not overwriting - file exists\n') %
508 ui.warn(_('%s: not overwriting - file exists\n') %
509 reltarget)
509 reltarget)
510 return
510 return
511 if not opts['after'] and not opts.get('dry_run'):
511 if not opts['after'] and not opts.get('dry_run'):
512 os.unlink(reltarget)
512 os.unlink(reltarget)
513 if opts['after']:
513 if opts['after']:
514 if not os.path.exists(reltarget):
514 if not os.path.exists(reltarget):
515 return
515 return
516 else:
516 else:
517 targetdir = os.path.dirname(reltarget) or '.'
517 targetdir = os.path.dirname(reltarget) or '.'
518 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'):
519 os.makedirs(targetdir)
519 os.makedirs(targetdir)
520 try:
520 try:
521 restore = repo.dirstate.state(abstarget) == 'r'
521 restore = repo.dirstate.state(abstarget) == 'r'
522 if restore and not opts.get('dry_run'):
522 if restore and not opts.get('dry_run'):
523 repo.undelete([abstarget], wlock)
523 repo.undelete([abstarget], wlock)
524 try:
524 try:
525 if not opts.get('dry_run'):
525 if not opts.get('dry_run'):
526 util.copyfile(relsrc, reltarget)
526 util.copyfile(relsrc, reltarget)
527 restore = False
527 restore = False
528 finally:
528 finally:
529 if restore:
529 if restore:
530 repo.remove([abstarget], wlock=wlock)
530 repo.remove([abstarget], wlock=wlock)
531 except IOError, inst:
531 except IOError, inst:
532 if inst.errno == errno.ENOENT:
532 if inst.errno == errno.ENOENT:
533 ui.warn(_('%s: deleted in working copy\n') % relsrc)
533 ui.warn(_('%s: deleted in working copy\n') % relsrc)
534 else:
534 else:
535 ui.warn(_('%s: cannot copy - %s\n') %
535 ui.warn(_('%s: cannot copy - %s\n') %
536 (relsrc, inst.strerror))
536 (relsrc, inst.strerror))
537 errors += 1
537 errors += 1
538 return
538 return
539 if ui.verbose or not exact:
539 if ui.verbose or not exact:
540 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
540 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
541 targets[abstarget] = abssrc
541 targets[abstarget] = abssrc
542 if abstarget != origsrc and not opts.get('dry_run'):
542 if abstarget != origsrc and not opts.get('dry_run'):
543 repo.copy(origsrc, abstarget, wlock)
543 repo.copy(origsrc, abstarget, wlock)
544 copied.append((abssrc, relsrc, exact))
544 copied.append((abssrc, relsrc, exact))
545
545
546 # pat: ossep
546 # pat: ossep
547 # dest ossep
547 # dest ossep
548 # srcs: list of (hgsep, hgsep, ossep, bool)
548 # srcs: list of (hgsep, hgsep, ossep, bool)
549 # return: function that takes hgsep and returns ossep
549 # return: function that takes hgsep and returns ossep
550 def targetpathfn(pat, dest, srcs):
550 def targetpathfn(pat, dest, srcs):
551 if os.path.isdir(pat):
551 if os.path.isdir(pat):
552 abspfx = util.canonpath(repo.root, cwd, pat)
552 abspfx = util.canonpath(repo.root, cwd, pat)
553 abspfx = util.localpath(abspfx)
553 abspfx = util.localpath(abspfx)
554 if destdirexists:
554 if destdirexists:
555 striplen = len(os.path.split(abspfx)[0])
555 striplen = len(os.path.split(abspfx)[0])
556 else:
556 else:
557 striplen = len(abspfx)
557 striplen = len(abspfx)
558 if striplen:
558 if striplen:
559 striplen += len(os.sep)
559 striplen += len(os.sep)
560 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
560 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
561 elif destdirexists:
561 elif destdirexists:
562 res = lambda p: os.path.join(dest,
562 res = lambda p: os.path.join(dest,
563 os.path.basename(util.localpath(p)))
563 os.path.basename(util.localpath(p)))
564 else:
564 else:
565 res = lambda p: dest
565 res = lambda p: dest
566 return res
566 return res
567
567
568 # pat: ossep
568 # pat: ossep
569 # dest ossep
569 # dest ossep
570 # srcs: list of (hgsep, hgsep, ossep, bool)
570 # srcs: list of (hgsep, hgsep, ossep, bool)
571 # return: function that takes hgsep and returns ossep
571 # return: function that takes hgsep and returns ossep
572 def targetpathafterfn(pat, dest, srcs):
572 def targetpathafterfn(pat, dest, srcs):
573 if util.patkind(pat, None)[0]:
573 if util.patkind(pat, None)[0]:
574 # a mercurial pattern
574 # a mercurial pattern
575 res = lambda p: os.path.join(dest,
575 res = lambda p: os.path.join(dest,
576 os.path.basename(util.localpath(p)))
576 os.path.basename(util.localpath(p)))
577 else:
577 else:
578 abspfx = util.canonpath(repo.root, cwd, pat)
578 abspfx = util.canonpath(repo.root, cwd, pat)
579 if len(abspfx) < len(srcs[0][0]):
579 if len(abspfx) < len(srcs[0][0]):
580 # A directory. Either the target path contains the last
580 # A directory. Either the target path contains the last
581 # component of the source path or it does not.
581 # component of the source path or it does not.
582 def evalpath(striplen):
582 def evalpath(striplen):
583 score = 0
583 score = 0
584 for s in srcs:
584 for s in srcs:
585 t = os.path.join(dest, util.localpath(s[0])[striplen:])
585 t = os.path.join(dest, util.localpath(s[0])[striplen:])
586 if os.path.exists(t):
586 if os.path.exists(t):
587 score += 1
587 score += 1
588 return score
588 return score
589
589
590 abspfx = util.localpath(abspfx)
590 abspfx = util.localpath(abspfx)
591 striplen = len(abspfx)
591 striplen = len(abspfx)
592 if striplen:
592 if striplen:
593 striplen += len(os.sep)
593 striplen += len(os.sep)
594 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])):
595 score = evalpath(striplen)
595 score = evalpath(striplen)
596 striplen1 = len(os.path.split(abspfx)[0])
596 striplen1 = len(os.path.split(abspfx)[0])
597 if striplen1:
597 if striplen1:
598 striplen1 += len(os.sep)
598 striplen1 += len(os.sep)
599 if evalpath(striplen1) > score:
599 if evalpath(striplen1) > score:
600 striplen = striplen1
600 striplen = striplen1
601 res = lambda p: os.path.join(dest,
601 res = lambda p: os.path.join(dest,
602 util.localpath(p)[striplen:])
602 util.localpath(p)[striplen:])
603 else:
603 else:
604 # a file
604 # a file
605 if destdirexists:
605 if destdirexists:
606 res = lambda p: os.path.join(dest,
606 res = lambda p: os.path.join(dest,
607 os.path.basename(util.localpath(p)))
607 os.path.basename(util.localpath(p)))
608 else:
608 else:
609 res = lambda p: dest
609 res = lambda p: dest
610 return res
610 return res
611
611
612
612
613 pats = util.expand_glob(pats)
613 pats = util.expand_glob(pats)
614 if not pats:
614 if not pats:
615 raise util.Abort(_('no source or destination specified'))
615 raise util.Abort(_('no source or destination specified'))
616 if len(pats) == 1:
616 if len(pats) == 1:
617 raise util.Abort(_('no destination specified'))
617 raise util.Abort(_('no destination specified'))
618 dest = pats.pop()
618 dest = pats.pop()
619 destdirexists = os.path.isdir(dest)
619 destdirexists = os.path.isdir(dest)
620 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:
621 raise util.Abort(_('with multiple sources, destination must be an '
621 raise util.Abort(_('with multiple sources, destination must be an '
622 'existing directory'))
622 'existing directory'))
623 if opts['after']:
623 if opts['after']:
624 tfn = targetpathafterfn
624 tfn = targetpathafterfn
625 else:
625 else:
626 tfn = targetpathfn
626 tfn = targetpathfn
627 copylist = []
627 copylist = []
628 for pat in pats:
628 for pat in pats:
629 srcs = []
629 srcs = []
630 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
630 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
631 globbed=True):
631 globbed=True):
632 origsrc = okaytocopy(abssrc, relsrc, exact)
632 origsrc = okaytocopy(abssrc, relsrc, exact)
633 if origsrc:
633 if origsrc:
634 srcs.append((origsrc, abssrc, relsrc, exact))
634 srcs.append((origsrc, abssrc, relsrc, exact))
635 if not srcs:
635 if not srcs:
636 continue
636 continue
637 copylist.append((tfn(pat, dest, srcs), srcs))
637 copylist.append((tfn(pat, dest, srcs), srcs))
638 if not copylist:
638 if not copylist:
639 raise util.Abort(_('no files to copy'))
639 raise util.Abort(_('no files to copy'))
640
640
641 for targetpath, srcs in copylist:
641 for targetpath, srcs in copylist:
642 for origsrc, abssrc, relsrc, exact in srcs:
642 for origsrc, abssrc, relsrc, exact in srcs:
643 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
643 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
644
644
645 if errors:
645 if errors:
646 ui.warn(_('(consider using --after)\n'))
646 ui.warn(_('(consider using --after)\n'))
647 return errors, copied
647 return errors, copied
648
648
649 def copy(ui, repo, *pats, **opts):
649 def copy(ui, repo, *pats, **opts):
650 """mark files as copied for the next commit
650 """mark files as copied for the next commit
651
651
652 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
653 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,
654 there can only be one source.
654 there can only be one source.
655
655
656 By default, this command copies the contents of files as they
656 By default, this command copies the contents of files as they
657 stand in the working directory. If invoked with --after, the
657 stand in the working directory. If invoked with --after, the
658 operation is recorded, but no copying is performed.
658 operation is recorded, but no copying is performed.
659
659
660 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
661 before that, see hg revert.
661 before that, see hg revert.
662 """
662 """
663 wlock = repo.wlock(0)
663 wlock = repo.wlock(0)
664 errs, copied = docopy(ui, repo, pats, opts, wlock)
664 errs, copied = docopy(ui, repo, pats, opts, wlock)
665 return errs
665 return errs
666
666
667 def debugancestor(ui, index, rev1, rev2):
667 def debugancestor(ui, index, rev1, rev2):
668 """find the ancestor revision of two revisions in a given index"""
668 """find the ancestor revision of two revisions in a given index"""
669 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
669 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
670 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
670 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
671 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
671 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
672
672
673 def debugcomplete(ui, cmd='', **opts):
673 def debugcomplete(ui, cmd='', **opts):
674 """returns the completion list associated with the given command"""
674 """returns the completion list associated with the given command"""
675
675
676 if opts['options']:
676 if opts['options']:
677 options = []
677 options = []
678 otables = [globalopts]
678 otables = [globalopts]
679 if cmd:
679 if cmd:
680 aliases, entry = findcmd(ui, cmd)
680 aliases, entry = findcmd(ui, cmd)
681 otables.append(entry[1])
681 otables.append(entry[1])
682 for t in otables:
682 for t in otables:
683 for o in t:
683 for o in t:
684 if o[0]:
684 if o[0]:
685 options.append('-%s' % o[0])
685 options.append('-%s' % o[0])
686 options.append('--%s' % o[1])
686 options.append('--%s' % o[1])
687 ui.write("%s\n" % "\n".join(options))
687 ui.write("%s\n" % "\n".join(options))
688 return
688 return
689
689
690 clist = findpossible(ui, cmd).keys()
690 clist = findpossible(ui, cmd).keys()
691 clist.sort()
691 clist.sort()
692 ui.write("%s\n" % "\n".join(clist))
692 ui.write("%s\n" % "\n".join(clist))
693
693
694 def debugrebuildstate(ui, repo, rev=""):
694 def debugrebuildstate(ui, repo, rev=""):
695 """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"""
696 if rev == "":
696 if rev == "":
697 rev = repo.changelog.tip()
697 rev = repo.changelog.tip()
698 ctx = repo.changectx(rev)
698 ctx = repo.changectx(rev)
699 files = ctx.manifest()
699 files = ctx.manifest()
700 wlock = repo.wlock()
700 wlock = repo.wlock()
701 repo.dirstate.rebuild(rev, files)
701 repo.dirstate.rebuild(rev, files)
702
702
703 def debugcheckstate(ui, repo):
703 def debugcheckstate(ui, repo):
704 """validate the correctness of the current dirstate"""
704 """validate the correctness of the current dirstate"""
705 parent1, parent2 = repo.dirstate.parents()
705 parent1, parent2 = repo.dirstate.parents()
706 repo.dirstate.read()
706 repo.dirstate.read()
707 dc = repo.dirstate.map
707 dc = repo.dirstate.map
708 keys = dc.keys()
708 keys = dc.keys()
709 keys.sort()
709 keys.sort()
710 m1 = repo.changectx(parent1).manifest()
710 m1 = repo.changectx(parent1).manifest()
711 m2 = repo.changectx(parent2).manifest()
711 m2 = repo.changectx(parent2).manifest()
712 errors = 0
712 errors = 0
713 for f in dc:
713 for f in dc:
714 state = repo.dirstate.state(f)
714 state = repo.dirstate.state(f)
715 if state in "nr" and f not in m1:
715 if state in "nr" and f not in m1:
716 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))
717 errors += 1
717 errors += 1
718 if state in "a" and f in m1:
718 if state in "a" and f in m1:
719 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))
720 errors += 1
720 errors += 1
721 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:
722 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") %
723 (f, state))
723 (f, state))
724 errors += 1
724 errors += 1
725 for f in m1:
725 for f in m1:
726 state = repo.dirstate.state(f)
726 state = repo.dirstate.state(f)
727 if state not in "nrm":
727 if state not in "nrm":
728 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))
729 errors += 1
729 errors += 1
730 if errors:
730 if errors:
731 error = _(".hg/dirstate inconsistent with current parent's manifest")
731 error = _(".hg/dirstate inconsistent with current parent's manifest")
732 raise util.Abort(error)
732 raise util.Abort(error)
733
733
734 def showconfig(ui, repo, *values, **opts):
734 def showconfig(ui, repo, *values, **opts):
735 """show combined config settings from all hgrc files
735 """show combined config settings from all hgrc files
736
736
737 With no args, print names and values of all config items.
737 With no args, print names and values of all config items.
738
738
739 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
740 that config item.
740 that config item.
741
741
742 With multiple args, print names and values of all config items
742 With multiple args, print names and values of all config items
743 with matching section names."""
743 with matching section names."""
744
744
745 untrusted = bool(opts.get('untrusted'))
745 untrusted = bool(opts.get('untrusted'))
746 if values:
746 if values:
747 if len([v for v in values if '.' in v]) > 1:
747 if len([v for v in values if '.' in v]) > 1:
748 raise util.Abort(_('only one config item permitted'))
748 raise util.Abort(_('only one config item permitted'))
749 for section, name, value in ui.walkconfig(untrusted=untrusted):
749 for section, name, value in ui.walkconfig(untrusted=untrusted):
750 sectname = section + '.' + name
750 sectname = section + '.' + name
751 if values:
751 if values:
752 for v in values:
752 for v in values:
753 if v == section:
753 if v == section:
754 ui.write('%s=%s\n' % (sectname, value))
754 ui.write('%s=%s\n' % (sectname, value))
755 elif v == sectname:
755 elif v == sectname:
756 ui.write(value, '\n')
756 ui.write(value, '\n')
757 else:
757 else:
758 ui.write('%s=%s\n' % (sectname, value))
758 ui.write('%s=%s\n' % (sectname, value))
759
759
760 def debugsetparents(ui, repo, rev1, rev2=None):
760 def debugsetparents(ui, repo, rev1, rev2=None):
761 """manually set the parents of the current working directory
761 """manually set the parents of the current working directory
762
762
763 This is useful for writing repository conversion tools, but should
763 This is useful for writing repository conversion tools, but should
764 be used with care.
764 be used with care.
765 """
765 """
766
766
767 if not rev2:
767 if not rev2:
768 rev2 = hex(nullid)
768 rev2 = hex(nullid)
769
769
770 wlock = repo.wlock()
770 wlock = repo.wlock()
771 try:
771 try:
772 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
772 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
773 finally:
773 finally:
774 wlock.release()
774 wlock.release()
775
775
776 def debugstate(ui, repo):
776 def debugstate(ui, repo):
777 """show the contents of the current dirstate"""
777 """show the contents of the current dirstate"""
778 repo.dirstate.read()
778 repo.dirstate.read()
779 dc = repo.dirstate.map
779 dc = repo.dirstate.map
780 keys = dc.keys()
780 keys = dc.keys()
781 keys.sort()
781 keys.sort()
782 for file_ in keys:
782 for file_ in keys:
783 if dc[file_][3] == -1:
783 if dc[file_][3] == -1:
784 # Pad or slice to locale representation
784 # Pad or slice to locale representation
785 locale_len = len(time.strftime("%x %X", time.localtime(0)))
785 locale_len = len(time.strftime("%x %X", time.localtime(0)))
786 timestr = 'unset'
786 timestr = 'unset'
787 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
787 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
788 else:
788 else:
789 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
789 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
790 ui.write("%c %3o %10d %s %s\n"
790 ui.write("%c %3o %10d %s %s\n"
791 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
791 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
792 timestr, file_))
792 timestr, file_))
793 for f in repo.dirstate.copies():
793 for f in repo.dirstate.copies():
794 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
794 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
795
795
796 def debugdata(ui, file_, rev):
796 def debugdata(ui, file_, rev):
797 """dump the contents of a data file revision"""
797 """dump the contents of a data file revision"""
798 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
798 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
799 try:
799 try:
800 ui.write(r.revision(r.lookup(rev)))
800 ui.write(r.revision(r.lookup(rev)))
801 except KeyError:
801 except KeyError:
802 raise util.Abort(_('invalid revision identifier %s') % rev)
802 raise util.Abort(_('invalid revision identifier %s') % rev)
803
803
804 def debugdate(ui, date, range=None, **opts):
804 def debugdate(ui, date, range=None, **opts):
805 """parse and display a date"""
805 """parse and display a date"""
806 if opts["extended"]:
806 if opts["extended"]:
807 d = util.parsedate(date, util.extendeddateformats)
807 d = util.parsedate(date, util.extendeddateformats)
808 else:
808 else:
809 d = util.parsedate(date)
809 d = util.parsedate(date)
810 ui.write("internal: %s %s\n" % d)
810 ui.write("internal: %s %s\n" % d)
811 ui.write("standard: %s\n" % util.datestr(d))
811 ui.write("standard: %s\n" % util.datestr(d))
812 if range:
812 if range:
813 m = util.matchdate(range)
813 m = util.matchdate(range)
814 ui.write("match: %s\n" % m(d[0]))
814 ui.write("match: %s\n" % m(d[0]))
815
815
816 def debugindex(ui, file_):
816 def debugindex(ui, file_):
817 """dump the contents of an index file"""
817 """dump the contents of an index file"""
818 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
818 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
819 ui.write(" rev offset length base linkrev" +
819 ui.write(" rev offset length base linkrev" +
820 " nodeid p1 p2\n")
820 " nodeid p1 p2\n")
821 for i in xrange(r.count()):
821 for i in xrange(r.count()):
822 node = r.node(i)
822 node = r.node(i)
823 pp = r.parents(node)
823 pp = r.parents(node)
824 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
824 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
825 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
825 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
826 short(node), short(pp[0]), short(pp[1])))
826 short(node), short(pp[0]), short(pp[1])))
827
827
828 def debugindexdot(ui, file_):
828 def debugindexdot(ui, file_):
829 """dump an index DAG as a .dot file"""
829 """dump an index DAG as a .dot file"""
830 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
830 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
831 ui.write("digraph G {\n")
831 ui.write("digraph G {\n")
832 for i in xrange(r.count()):
832 for i in xrange(r.count()):
833 node = r.node(i)
833 node = r.node(i)
834 pp = r.parents(node)
834 pp = r.parents(node)
835 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
835 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
836 if pp[1] != nullid:
836 if pp[1] != nullid:
837 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
837 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
838 ui.write("}\n")
838 ui.write("}\n")
839
839
840 def debuginstall(ui):
840 def debuginstall(ui):
841 '''test Mercurial installation'''
841 '''test Mercurial installation'''
842
842
843 def writetemp(contents):
843 def writetemp(contents):
844 (fd, name) = tempfile.mkstemp()
844 (fd, name) = tempfile.mkstemp()
845 f = os.fdopen(fd, "wb")
845 f = os.fdopen(fd, "wb")
846 f.write(contents)
846 f.write(contents)
847 f.close()
847 f.close()
848 return name
848 return name
849
849
850 problems = 0
850 problems = 0
851
851
852 # encoding
852 # encoding
853 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
853 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
854 try:
854 try:
855 util.fromlocal("test")
855 util.fromlocal("test")
856 except util.Abort, inst:
856 except util.Abort, inst:
857 ui.write(" %s\n" % inst)
857 ui.write(" %s\n" % inst)
858 ui.write(_(" (check that your locale is properly set)\n"))
858 ui.write(_(" (check that your locale is properly set)\n"))
859 problems += 1
859 problems += 1
860
860
861 # compiled modules
861 # compiled modules
862 ui.status(_("Checking extensions...\n"))
862 ui.status(_("Checking extensions...\n"))
863 try:
863 try:
864 import bdiff, mpatch, base85
864 import bdiff, mpatch, base85
865 except Exception, inst:
865 except Exception, inst:
866 ui.write(" %s\n" % inst)
866 ui.write(" %s\n" % inst)
867 ui.write(_(" One or more extensions could not be found"))
867 ui.write(_(" One or more extensions could not be found"))
868 ui.write(_(" (check that you compiled the extensions)\n"))
868 ui.write(_(" (check that you compiled the extensions)\n"))
869 problems += 1
869 problems += 1
870
870
871 # templates
871 # templates
872 ui.status(_("Checking templates...\n"))
872 ui.status(_("Checking templates...\n"))
873 try:
873 try:
874 import templater
874 import templater
875 t = templater.templater(templater.templatepath("map-cmdline.default"))
875 t = templater.templater(templater.templatepath("map-cmdline.default"))
876 except Exception, inst:
876 except Exception, inst:
877 ui.write(" %s\n" % inst)
877 ui.write(" %s\n" % inst)
878 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
878 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
879 problems += 1
879 problems += 1
880
880
881 # patch
881 # patch
882 ui.status(_("Checking patch...\n"))
882 ui.status(_("Checking patch...\n"))
883 path = os.environ.get('PATH', '')
884 patcher = ui.config('ui', 'patch')
883 patcher = ui.config('ui', 'patch')
885 if not patcher:
884 patcher = ((patcher and util.find_exe(patcher)) or
886 patcher = util.find_in_path('gpatch', path,
885 util.find_exe('gpatch') or
887 util.find_in_path('patch', path, None))
886 util.find_exe('patch'))
888 if not patcher:
887 if not patcher:
889 ui.write(_(" Can't find patch or gpatch in PATH\n"))
888 ui.write(_(" Can't find patch or gpatch in PATH\n"))
890 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
889 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
891 problems += 1
890 problems += 1
892 else:
891 else:
893 # actually attempt a patch here
892 # actually attempt a patch here
894 a = "1\n2\n3\n4\n"
893 a = "1\n2\n3\n4\n"
895 b = "1\n2\n3\ninsert\n4\n"
894 b = "1\n2\n3\ninsert\n4\n"
896 fa = writetemp(a)
895 fa = writetemp(a)
897 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa))
896 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa))
898 fd = writetemp(d)
897 fd = writetemp(d)
899
898
900 files = {}
899 files = {}
901 try:
900 try:
902 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
901 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
903 except util.Abort, e:
902 except util.Abort, e:
904 ui.write(_(" patch call failed:\n"))
903 ui.write(_(" patch call failed:\n"))
905 ui.write(" " + str(e) + "\n")
904 ui.write(" " + str(e) + "\n")
906 problems += 1
905 problems += 1
907 else:
906 else:
908 if list(files) != [os.path.basename(fa)]:
907 if list(files) != [os.path.basename(fa)]:
909 ui.write(_(" unexpected patch output!"))
908 ui.write(_(" unexpected patch output!"))
910 ui.write(_(" (you may have an incompatible version of patch)\n"))
909 ui.write(_(" (you may have an incompatible version of patch)\n"))
911 problems += 1
910 problems += 1
912 a = file(fa).read()
911 a = file(fa).read()
913 if a != b:
912 if a != b:
914 ui.write(_(" patch test failed!"))
913 ui.write(_(" patch test failed!"))
915 ui.write(_(" (you may have an incompatible version of patch)\n"))
914 ui.write(_(" (you may have an incompatible version of patch)\n"))
916 problems += 1
915 problems += 1
917
916
918 os.unlink(fa)
917 os.unlink(fa)
919 os.unlink(fd)
918 os.unlink(fd)
920
919
921 # merge helper
920 # merge helper
922 ui.status(_("Checking merge helper...\n"))
921 ui.status(_("Checking merge helper...\n"))
923 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
922 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
924 or "hgmerge")
923 or "hgmerge")
925 cmdpath = util.find_in_path(cmd, path)
924 cmdpath = util.find_exe(cmd) or util.find_exe(cmd.split()[0])
926 if not cmdpath:
927 cmdpath = util.find_in_path(cmd.split()[0], path)
928 if not cmdpath:
925 if not cmdpath:
929 if cmd == 'hgmerge':
926 if cmd == 'hgmerge':
930 ui.write(_(" No merge helper set and can't find default"
927 ui.write(_(" No merge helper set and can't find default"
931 " hgmerge script in PATH\n"))
928 " hgmerge script in PATH\n"))
932 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
929 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
933 else:
930 else:
934 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
931 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
935 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
932 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
936 problems += 1
933 problems += 1
937 else:
934 else:
938 # actually attempt a patch here
935 # actually attempt a patch here
939 fa = writetemp("1\n2\n3\n4\n")
936 fa = writetemp("1\n2\n3\n4\n")
940 fl = writetemp("1\n2\n3\ninsert\n4\n")
937 fl = writetemp("1\n2\n3\ninsert\n4\n")
941 fr = writetemp("begin\n1\n2\n3\n4\n")
938 fr = writetemp("begin\n1\n2\n3\n4\n")
942 r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
939 r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
943 if r:
940 if r:
944 ui.write(_(" got unexpected merge error %d!") % r)
941 ui.write(_(" got unexpected merge error %d!") % r)
945 problems += 1
942 problems += 1
946 m = file(fl).read()
943 m = file(fl).read()
947 if m != "begin\n1\n2\n3\ninsert\n4\n":
944 if m != "begin\n1\n2\n3\ninsert\n4\n":
948 ui.write(_(" got unexpected merge results!") % r)
945 ui.write(_(" got unexpected merge results!") % r)
949 ui.write(_(" (your merge helper may have the"
946 ui.write(_(" (your merge helper may have the"
950 " wrong argument order)\n"))
947 " wrong argument order)\n"))
951 ui.write(m)
948 ui.write(m)
952 os.unlink(fa)
949 os.unlink(fa)
953 os.unlink(fl)
950 os.unlink(fl)
954 os.unlink(fr)
951 os.unlink(fr)
955
952
956 # editor
953 # editor
957 ui.status(_("Checking commit editor...\n"))
954 ui.status(_("Checking commit editor...\n"))
958 editor = (os.environ.get("HGEDITOR") or
955 editor = (os.environ.get("HGEDITOR") or
959 ui.config("ui", "editor") or
956 ui.config("ui", "editor") or
960 os.environ.get("EDITOR", "vi"))
957 os.environ.get("EDITOR", "vi"))
961 cmdpath = util.find_in_path(editor, path)
958 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
962 if not cmdpath:
963 cmdpath = util.find_in_path(editor.split()[0], path)
964 if not cmdpath:
959 if not cmdpath:
965 if editor == 'vi':
960 if editor == 'vi':
966 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
961 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
967 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
962 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
968 else:
963 else:
969 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
964 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
970 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
965 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
971 problems += 1
966 problems += 1
972
967
973 # check username
968 # check username
974 ui.status(_("Checking username...\n"))
969 ui.status(_("Checking username...\n"))
975 user = os.environ.get("HGUSER")
970 user = os.environ.get("HGUSER")
976 if user is None:
971 if user is None:
977 user = ui.config("ui", "username")
972 user = ui.config("ui", "username")
978 if user is None:
973 if user is None:
979 user = os.environ.get("EMAIL")
974 user = os.environ.get("EMAIL")
980 if not user:
975 if not user:
981 ui.warn(" ")
976 ui.warn(" ")
982 ui.username()
977 ui.username()
983 ui.write(_(" (specify a username in your .hgrc file)\n"))
978 ui.write(_(" (specify a username in your .hgrc file)\n"))
984
979
985 if not problems:
980 if not problems:
986 ui.status(_("No problems detected\n"))
981 ui.status(_("No problems detected\n"))
987 else:
982 else:
988 ui.write(_("%s problems detected,"
983 ui.write(_("%s problems detected,"
989 " please check your install!\n") % problems)
984 " please check your install!\n") % problems)
990
985
991 return problems
986 return problems
992
987
993 def debugrename(ui, repo, file1, *pats, **opts):
988 def debugrename(ui, repo, file1, *pats, **opts):
994 """dump rename information"""
989 """dump rename information"""
995
990
996 ctx = repo.changectx(opts.get('rev', 'tip'))
991 ctx = repo.changectx(opts.get('rev', 'tip'))
997 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
992 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
998 ctx.node()):
993 ctx.node()):
999 m = ctx.filectx(abs).renamed()
994 m = ctx.filectx(abs).renamed()
1000 if m:
995 if m:
1001 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
996 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
1002 else:
997 else:
1003 ui.write(_("%s not renamed\n") % rel)
998 ui.write(_("%s not renamed\n") % rel)
1004
999
1005 def debugwalk(ui, repo, *pats, **opts):
1000 def debugwalk(ui, repo, *pats, **opts):
1006 """show how files match on given patterns"""
1001 """show how files match on given patterns"""
1007 items = list(cmdutil.walk(repo, pats, opts))
1002 items = list(cmdutil.walk(repo, pats, opts))
1008 if not items:
1003 if not items:
1009 return
1004 return
1010 fmt = '%%s %%-%ds %%-%ds %%s' % (
1005 fmt = '%%s %%-%ds %%-%ds %%s' % (
1011 max([len(abs) for (src, abs, rel, exact) in items]),
1006 max([len(abs) for (src, abs, rel, exact) in items]),
1012 max([len(rel) for (src, abs, rel, exact) in items]))
1007 max([len(rel) for (src, abs, rel, exact) in items]))
1013 for src, abs, rel, exact in items:
1008 for src, abs, rel, exact in items:
1014 line = fmt % (src, abs, rel, exact and 'exact' or '')
1009 line = fmt % (src, abs, rel, exact and 'exact' or '')
1015 ui.write("%s\n" % line.rstrip())
1010 ui.write("%s\n" % line.rstrip())
1016
1011
1017 def diff(ui, repo, *pats, **opts):
1012 def diff(ui, repo, *pats, **opts):
1018 """diff repository (or selected files)
1013 """diff repository (or selected files)
1019
1014
1020 Show differences between revisions for the specified files.
1015 Show differences between revisions for the specified files.
1021
1016
1022 Differences between files are shown using the unified diff format.
1017 Differences between files are shown using the unified diff format.
1023
1018
1024 NOTE: diff may generate unexpected results for merges, as it will
1019 NOTE: diff may generate unexpected results for merges, as it will
1025 default to comparing against the working directory's first parent
1020 default to comparing against the working directory's first parent
1026 changeset if no revisions are specified.
1021 changeset if no revisions are specified.
1027
1022
1028 When two revision arguments are given, then changes are shown
1023 When two revision arguments are given, then changes are shown
1029 between those revisions. If only one revision is specified then
1024 between those revisions. If only one revision is specified then
1030 that revision is compared to the working directory, and, when no
1025 that revision is compared to the working directory, and, when no
1031 revisions are specified, the working directory files are compared
1026 revisions are specified, the working directory files are compared
1032 to its parent.
1027 to its parent.
1033
1028
1034 Without the -a option, diff will avoid generating diffs of files
1029 Without the -a option, diff will avoid generating diffs of files
1035 it detects as binary. With -a, diff will generate a diff anyway,
1030 it detects as binary. With -a, diff will generate a diff anyway,
1036 probably with undesirable results.
1031 probably with undesirable results.
1037 """
1032 """
1038 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1033 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1039
1034
1040 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1035 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1041
1036
1042 patch.diff(repo, node1, node2, fns, match=matchfn,
1037 patch.diff(repo, node1, node2, fns, match=matchfn,
1043 opts=patch.diffopts(ui, opts))
1038 opts=patch.diffopts(ui, opts))
1044
1039
1045 def export(ui, repo, *changesets, **opts):
1040 def export(ui, repo, *changesets, **opts):
1046 """dump the header and diffs for one or more changesets
1041 """dump the header and diffs for one or more changesets
1047
1042
1048 Print the changeset header and diffs for one or more revisions.
1043 Print the changeset header and diffs for one or more revisions.
1049
1044
1050 The information shown in the changeset header is: author,
1045 The information shown in the changeset header is: author,
1051 changeset hash, parent(s) and commit comment.
1046 changeset hash, parent(s) and commit comment.
1052
1047
1053 NOTE: export may generate unexpected diff output for merge changesets,
1048 NOTE: export may generate unexpected diff output for merge changesets,
1054 as it will compare the merge changeset against its first parent only.
1049 as it will compare the merge changeset against its first parent only.
1055
1050
1056 Output may be to a file, in which case the name of the file is
1051 Output may be to a file, in which case the name of the file is
1057 given using a format string. The formatting rules are as follows:
1052 given using a format string. The formatting rules are as follows:
1058
1053
1059 %% literal "%" character
1054 %% literal "%" character
1060 %H changeset hash (40 bytes of hexadecimal)
1055 %H changeset hash (40 bytes of hexadecimal)
1061 %N number of patches being generated
1056 %N number of patches being generated
1062 %R changeset revision number
1057 %R changeset revision number
1063 %b basename of the exporting repository
1058 %b basename of the exporting repository
1064 %h short-form changeset hash (12 bytes of hexadecimal)
1059 %h short-form changeset hash (12 bytes of hexadecimal)
1065 %n zero-padded sequence number, starting at 1
1060 %n zero-padded sequence number, starting at 1
1066 %r zero-padded changeset revision number
1061 %r zero-padded changeset revision number
1067
1062
1068 Without the -a option, export will avoid generating diffs of files
1063 Without the -a option, export will avoid generating diffs of files
1069 it detects as binary. With -a, export will generate a diff anyway,
1064 it detects as binary. With -a, export will generate a diff anyway,
1070 probably with undesirable results.
1065 probably with undesirable results.
1071
1066
1072 With the --switch-parent option, the diff will be against the second
1067 With the --switch-parent option, the diff will be against the second
1073 parent. It can be useful to review a merge.
1068 parent. It can be useful to review a merge.
1074 """
1069 """
1075 if not changesets:
1070 if not changesets:
1076 raise util.Abort(_("export requires at least one changeset"))
1071 raise util.Abort(_("export requires at least one changeset"))
1077 revs = cmdutil.revrange(repo, changesets)
1072 revs = cmdutil.revrange(repo, changesets)
1078 if len(revs) > 1:
1073 if len(revs) > 1:
1079 ui.note(_('exporting patches:\n'))
1074 ui.note(_('exporting patches:\n'))
1080 else:
1075 else:
1081 ui.note(_('exporting patch:\n'))
1076 ui.note(_('exporting patch:\n'))
1082 patch.export(repo, revs, template=opts['output'],
1077 patch.export(repo, revs, template=opts['output'],
1083 switch_parent=opts['switch_parent'],
1078 switch_parent=opts['switch_parent'],
1084 opts=patch.diffopts(ui, opts))
1079 opts=patch.diffopts(ui, opts))
1085
1080
1086 def grep(ui, repo, pattern, *pats, **opts):
1081 def grep(ui, repo, pattern, *pats, **opts):
1087 """search for a pattern in specified files and revisions
1082 """search for a pattern in specified files and revisions
1088
1083
1089 Search revisions of files for a regular expression.
1084 Search revisions of files for a regular expression.
1090
1085
1091 This command behaves differently than Unix grep. It only accepts
1086 This command behaves differently than Unix grep. It only accepts
1092 Python/Perl regexps. It searches repository history, not the
1087 Python/Perl regexps. It searches repository history, not the
1093 working directory. It always prints the revision number in which
1088 working directory. It always prints the revision number in which
1094 a match appears.
1089 a match appears.
1095
1090
1096 By default, grep only prints output for the first revision of a
1091 By default, grep only prints output for the first revision of a
1097 file in which it finds a match. To get it to print every revision
1092 file in which it finds a match. To get it to print every revision
1098 that contains a change in match status ("-" for a match that
1093 that contains a change in match status ("-" for a match that
1099 becomes a non-match, or "+" for a non-match that becomes a match),
1094 becomes a non-match, or "+" for a non-match that becomes a match),
1100 use the --all flag.
1095 use the --all flag.
1101 """
1096 """
1102 reflags = 0
1097 reflags = 0
1103 if opts['ignore_case']:
1098 if opts['ignore_case']:
1104 reflags |= re.I
1099 reflags |= re.I
1105 regexp = re.compile(pattern, reflags)
1100 regexp = re.compile(pattern, reflags)
1106 sep, eol = ':', '\n'
1101 sep, eol = ':', '\n'
1107 if opts['print0']:
1102 if opts['print0']:
1108 sep = eol = '\0'
1103 sep = eol = '\0'
1109
1104
1110 fcache = {}
1105 fcache = {}
1111 def getfile(fn):
1106 def getfile(fn):
1112 if fn not in fcache:
1107 if fn not in fcache:
1113 fcache[fn] = repo.file(fn)
1108 fcache[fn] = repo.file(fn)
1114 return fcache[fn]
1109 return fcache[fn]
1115
1110
1116 def matchlines(body):
1111 def matchlines(body):
1117 begin = 0
1112 begin = 0
1118 linenum = 0
1113 linenum = 0
1119 while True:
1114 while True:
1120 match = regexp.search(body, begin)
1115 match = regexp.search(body, begin)
1121 if not match:
1116 if not match:
1122 break
1117 break
1123 mstart, mend = match.span()
1118 mstart, mend = match.span()
1124 linenum += body.count('\n', begin, mstart) + 1
1119 linenum += body.count('\n', begin, mstart) + 1
1125 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1120 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1126 lend = body.find('\n', mend)
1121 lend = body.find('\n', mend)
1127 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1122 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1128 begin = lend + 1
1123 begin = lend + 1
1129
1124
1130 class linestate(object):
1125 class linestate(object):
1131 def __init__(self, line, linenum, colstart, colend):
1126 def __init__(self, line, linenum, colstart, colend):
1132 self.line = line
1127 self.line = line
1133 self.linenum = linenum
1128 self.linenum = linenum
1134 self.colstart = colstart
1129 self.colstart = colstart
1135 self.colend = colend
1130 self.colend = colend
1136
1131
1137 def __eq__(self, other):
1132 def __eq__(self, other):
1138 return self.line == other.line
1133 return self.line == other.line
1139
1134
1140 matches = {}
1135 matches = {}
1141 copies = {}
1136 copies = {}
1142 def grepbody(fn, rev, body):
1137 def grepbody(fn, rev, body):
1143 matches[rev].setdefault(fn, [])
1138 matches[rev].setdefault(fn, [])
1144 m = matches[rev][fn]
1139 m = matches[rev][fn]
1145 for lnum, cstart, cend, line in matchlines(body):
1140 for lnum, cstart, cend, line in matchlines(body):
1146 s = linestate(line, lnum, cstart, cend)
1141 s = linestate(line, lnum, cstart, cend)
1147 m.append(s)
1142 m.append(s)
1148
1143
1149 def difflinestates(a, b):
1144 def difflinestates(a, b):
1150 sm = difflib.SequenceMatcher(None, a, b)
1145 sm = difflib.SequenceMatcher(None, a, b)
1151 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1146 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1152 if tag == 'insert':
1147 if tag == 'insert':
1153 for i in xrange(blo, bhi):
1148 for i in xrange(blo, bhi):
1154 yield ('+', b[i])
1149 yield ('+', b[i])
1155 elif tag == 'delete':
1150 elif tag == 'delete':
1156 for i in xrange(alo, ahi):
1151 for i in xrange(alo, ahi):
1157 yield ('-', a[i])
1152 yield ('-', a[i])
1158 elif tag == 'replace':
1153 elif tag == 'replace':
1159 for i in xrange(alo, ahi):
1154 for i in xrange(alo, ahi):
1160 yield ('-', a[i])
1155 yield ('-', a[i])
1161 for i in xrange(blo, bhi):
1156 for i in xrange(blo, bhi):
1162 yield ('+', b[i])
1157 yield ('+', b[i])
1163
1158
1164 prev = {}
1159 prev = {}
1165 def display(fn, rev, states, prevstates):
1160 def display(fn, rev, states, prevstates):
1166 found = False
1161 found = False
1167 filerevmatches = {}
1162 filerevmatches = {}
1168 r = prev.get(fn, -1)
1163 r = prev.get(fn, -1)
1169 if opts['all']:
1164 if opts['all']:
1170 iter = difflinestates(states, prevstates)
1165 iter = difflinestates(states, prevstates)
1171 else:
1166 else:
1172 iter = [('', l) for l in prevstates]
1167 iter = [('', l) for l in prevstates]
1173 for change, l in iter:
1168 for change, l in iter:
1174 cols = [fn, str(r)]
1169 cols = [fn, str(r)]
1175 if opts['line_number']:
1170 if opts['line_number']:
1176 cols.append(str(l.linenum))
1171 cols.append(str(l.linenum))
1177 if opts['all']:
1172 if opts['all']:
1178 cols.append(change)
1173 cols.append(change)
1179 if opts['user']:
1174 if opts['user']:
1180 cols.append(ui.shortuser(get(r)[1]))
1175 cols.append(ui.shortuser(get(r)[1]))
1181 if opts['files_with_matches']:
1176 if opts['files_with_matches']:
1182 c = (fn, r)
1177 c = (fn, r)
1183 if c in filerevmatches:
1178 if c in filerevmatches:
1184 continue
1179 continue
1185 filerevmatches[c] = 1
1180 filerevmatches[c] = 1
1186 else:
1181 else:
1187 cols.append(l.line)
1182 cols.append(l.line)
1188 ui.write(sep.join(cols), eol)
1183 ui.write(sep.join(cols), eol)
1189 found = True
1184 found = True
1190 return found
1185 return found
1191
1186
1192 fstate = {}
1187 fstate = {}
1193 skip = {}
1188 skip = {}
1194 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1189 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1195 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1190 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1196 found = False
1191 found = False
1197 follow = opts.get('follow')
1192 follow = opts.get('follow')
1198 for st, rev, fns in changeiter:
1193 for st, rev, fns in changeiter:
1199 if st == 'window':
1194 if st == 'window':
1200 matches.clear()
1195 matches.clear()
1201 elif st == 'add':
1196 elif st == 'add':
1202 mf = repo.changectx(rev).manifest()
1197 mf = repo.changectx(rev).manifest()
1203 matches[rev] = {}
1198 matches[rev] = {}
1204 for fn in fns:
1199 for fn in fns:
1205 if fn in skip:
1200 if fn in skip:
1206 continue
1201 continue
1207 fstate.setdefault(fn, {})
1202 fstate.setdefault(fn, {})
1208 try:
1203 try:
1209 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1204 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1210 if follow:
1205 if follow:
1211 copied = getfile(fn).renamed(mf[fn])
1206 copied = getfile(fn).renamed(mf[fn])
1212 if copied:
1207 if copied:
1213 copies.setdefault(rev, {})[fn] = copied[0]
1208 copies.setdefault(rev, {})[fn] = copied[0]
1214 except KeyError:
1209 except KeyError:
1215 pass
1210 pass
1216 elif st == 'iter':
1211 elif st == 'iter':
1217 states = matches[rev].items()
1212 states = matches[rev].items()
1218 states.sort()
1213 states.sort()
1219 for fn, m in states:
1214 for fn, m in states:
1220 copy = copies.get(rev, {}).get(fn)
1215 copy = copies.get(rev, {}).get(fn)
1221 if fn in skip:
1216 if fn in skip:
1222 if copy:
1217 if copy:
1223 skip[copy] = True
1218 skip[copy] = True
1224 continue
1219 continue
1225 if fn in prev or fstate[fn]:
1220 if fn in prev or fstate[fn]:
1226 r = display(fn, rev, m, fstate[fn])
1221 r = display(fn, rev, m, fstate[fn])
1227 found = found or r
1222 found = found or r
1228 if r and not opts['all']:
1223 if r and not opts['all']:
1229 skip[fn] = True
1224 skip[fn] = True
1230 if copy:
1225 if copy:
1231 skip[copy] = True
1226 skip[copy] = True
1232 fstate[fn] = m
1227 fstate[fn] = m
1233 if copy:
1228 if copy:
1234 fstate[copy] = m
1229 fstate[copy] = m
1235 prev[fn] = rev
1230 prev[fn] = rev
1236
1231
1237 fstate = fstate.items()
1232 fstate = fstate.items()
1238 fstate.sort()
1233 fstate.sort()
1239 for fn, state in fstate:
1234 for fn, state in fstate:
1240 if fn in skip:
1235 if fn in skip:
1241 continue
1236 continue
1242 if fn not in copies.get(prev[fn], {}):
1237 if fn not in copies.get(prev[fn], {}):
1243 found = display(fn, rev, {}, state) or found
1238 found = display(fn, rev, {}, state) or found
1244 return (not found and 1) or 0
1239 return (not found and 1) or 0
1245
1240
1246 def heads(ui, repo, **opts):
1241 def heads(ui, repo, **opts):
1247 """show current repository heads
1242 """show current repository heads
1248
1243
1249 Show all repository head changesets.
1244 Show all repository head changesets.
1250
1245
1251 Repository "heads" are changesets that don't have children
1246 Repository "heads" are changesets that don't have children
1252 changesets. They are where development generally takes place and
1247 changesets. They are where development generally takes place and
1253 are the usual targets for update and merge operations.
1248 are the usual targets for update and merge operations.
1254 """
1249 """
1255 if opts['rev']:
1250 if opts['rev']:
1256 heads = repo.heads(repo.lookup(opts['rev']))
1251 heads = repo.heads(repo.lookup(opts['rev']))
1257 else:
1252 else:
1258 heads = repo.heads()
1253 heads = repo.heads()
1259 displayer = cmdutil.show_changeset(ui, repo, opts)
1254 displayer = cmdutil.show_changeset(ui, repo, opts)
1260 for n in heads:
1255 for n in heads:
1261 displayer.show(changenode=n)
1256 displayer.show(changenode=n)
1262
1257
1263 def help_(ui, name=None, with_version=False):
1258 def help_(ui, name=None, with_version=False):
1264 """show help for a command, extension, or list of commands
1259 """show help for a command, extension, or list of commands
1265
1260
1266 With no arguments, print a list of commands and short help.
1261 With no arguments, print a list of commands and short help.
1267
1262
1268 Given a command name, print help for that command.
1263 Given a command name, print help for that command.
1269
1264
1270 Given an extension name, print help for that extension, and the
1265 Given an extension name, print help for that extension, and the
1271 commands it provides."""
1266 commands it provides."""
1272 option_lists = []
1267 option_lists = []
1273
1268
1274 def addglobalopts(aliases):
1269 def addglobalopts(aliases):
1275 if ui.verbose:
1270 if ui.verbose:
1276 option_lists.append((_("global options:"), globalopts))
1271 option_lists.append((_("global options:"), globalopts))
1277 if name == 'shortlist':
1272 if name == 'shortlist':
1278 option_lists.append((_('use "hg help" for the full list '
1273 option_lists.append((_('use "hg help" for the full list '
1279 'of commands'), ()))
1274 'of commands'), ()))
1280 else:
1275 else:
1281 if name == 'shortlist':
1276 if name == 'shortlist':
1282 msg = _('use "hg help" for the full list of commands '
1277 msg = _('use "hg help" for the full list of commands '
1283 'or "hg -v" for details')
1278 'or "hg -v" for details')
1284 elif aliases:
1279 elif aliases:
1285 msg = _('use "hg -v help%s" to show aliases and '
1280 msg = _('use "hg -v help%s" to show aliases and '
1286 'global options') % (name and " " + name or "")
1281 'global options') % (name and " " + name or "")
1287 else:
1282 else:
1288 msg = _('use "hg -v help %s" to show global options') % name
1283 msg = _('use "hg -v help %s" to show global options') % name
1289 option_lists.append((msg, ()))
1284 option_lists.append((msg, ()))
1290
1285
1291 def helpcmd(name):
1286 def helpcmd(name):
1292 if with_version:
1287 if with_version:
1293 version_(ui)
1288 version_(ui)
1294 ui.write('\n')
1289 ui.write('\n')
1295 aliases, i = findcmd(ui, name)
1290 aliases, i = findcmd(ui, name)
1296 # synopsis
1291 # synopsis
1297 ui.write("%s\n\n" % i[2])
1292 ui.write("%s\n\n" % i[2])
1298
1293
1299 # description
1294 # description
1300 doc = i[0].__doc__
1295 doc = i[0].__doc__
1301 if not doc:
1296 if not doc:
1302 doc = _("(No help text available)")
1297 doc = _("(No help text available)")
1303 if ui.quiet:
1298 if ui.quiet:
1304 doc = doc.splitlines(0)[0]
1299 doc = doc.splitlines(0)[0]
1305 ui.write("%s\n" % doc.rstrip())
1300 ui.write("%s\n" % doc.rstrip())
1306
1301
1307 if not ui.quiet:
1302 if not ui.quiet:
1308 # aliases
1303 # aliases
1309 if len(aliases) > 1:
1304 if len(aliases) > 1:
1310 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1305 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1311
1306
1312 # options
1307 # options
1313 if i[1]:
1308 if i[1]:
1314 option_lists.append((_("options:\n"), i[1]))
1309 option_lists.append((_("options:\n"), i[1]))
1315
1310
1316 addglobalopts(False)
1311 addglobalopts(False)
1317
1312
1318 def helplist(select=None):
1313 def helplist(select=None):
1319 h = {}
1314 h = {}
1320 cmds = {}
1315 cmds = {}
1321 for c, e in table.items():
1316 for c, e in table.items():
1322 f = c.split("|", 1)[0]
1317 f = c.split("|", 1)[0]
1323 if select and not select(f):
1318 if select and not select(f):
1324 continue
1319 continue
1325 if name == "shortlist" and not f.startswith("^"):
1320 if name == "shortlist" and not f.startswith("^"):
1326 continue
1321 continue
1327 f = f.lstrip("^")
1322 f = f.lstrip("^")
1328 if not ui.debugflag and f.startswith("debug"):
1323 if not ui.debugflag and f.startswith("debug"):
1329 continue
1324 continue
1330 doc = e[0].__doc__
1325 doc = e[0].__doc__
1331 if not doc:
1326 if not doc:
1332 doc = _("(No help text available)")
1327 doc = _("(No help text available)")
1333 h[f] = doc.splitlines(0)[0].rstrip()
1328 h[f] = doc.splitlines(0)[0].rstrip()
1334 cmds[f] = c.lstrip("^")
1329 cmds[f] = c.lstrip("^")
1335
1330
1336 fns = h.keys()
1331 fns = h.keys()
1337 fns.sort()
1332 fns.sort()
1338 m = max(map(len, fns))
1333 m = max(map(len, fns))
1339 for f in fns:
1334 for f in fns:
1340 if ui.verbose:
1335 if ui.verbose:
1341 commands = cmds[f].replace("|",", ")
1336 commands = cmds[f].replace("|",", ")
1342 ui.write(" %s:\n %s\n"%(commands, h[f]))
1337 ui.write(" %s:\n %s\n"%(commands, h[f]))
1343 else:
1338 else:
1344 ui.write(' %-*s %s\n' % (m, f, h[f]))
1339 ui.write(' %-*s %s\n' % (m, f, h[f]))
1345
1340
1346 if not ui.quiet:
1341 if not ui.quiet:
1347 addglobalopts(True)
1342 addglobalopts(True)
1348
1343
1349 def helptopic(name):
1344 def helptopic(name):
1350 v = None
1345 v = None
1351 for i in help.helptable:
1346 for i in help.helptable:
1352 l = i.split('|')
1347 l = i.split('|')
1353 if name in l:
1348 if name in l:
1354 v = i
1349 v = i
1355 header = l[-1]
1350 header = l[-1]
1356 if not v:
1351 if not v:
1357 raise UnknownCommand(name)
1352 raise UnknownCommand(name)
1358
1353
1359 # description
1354 # description
1360 doc = help.helptable[v]
1355 doc = help.helptable[v]
1361 if not doc:
1356 if not doc:
1362 doc = _("(No help text available)")
1357 doc = _("(No help text available)")
1363 if callable(doc):
1358 if callable(doc):
1364 doc = doc()
1359 doc = doc()
1365
1360
1366 ui.write("%s\n" % header)
1361 ui.write("%s\n" % header)
1367 ui.write("%s\n" % doc.rstrip())
1362 ui.write("%s\n" % doc.rstrip())
1368
1363
1369 def helpext(name):
1364 def helpext(name):
1370 try:
1365 try:
1371 mod = findext(name)
1366 mod = findext(name)
1372 except KeyError:
1367 except KeyError:
1373 raise UnknownCommand(name)
1368 raise UnknownCommand(name)
1374
1369
1375 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1370 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1376 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1371 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1377 for d in doc[1:]:
1372 for d in doc[1:]:
1378 ui.write(d, '\n')
1373 ui.write(d, '\n')
1379
1374
1380 ui.status('\n')
1375 ui.status('\n')
1381
1376
1382 try:
1377 try:
1383 ct = mod.cmdtable
1378 ct = mod.cmdtable
1384 except AttributeError:
1379 except AttributeError:
1385 ui.status(_('no commands defined\n'))
1380 ui.status(_('no commands defined\n'))
1386 return
1381 return
1387
1382
1388 ui.status(_('list of commands:\n\n'))
1383 ui.status(_('list of commands:\n\n'))
1389 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1384 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1390 helplist(modcmds.has_key)
1385 helplist(modcmds.has_key)
1391
1386
1392 if name and name != 'shortlist':
1387 if name and name != 'shortlist':
1393 i = None
1388 i = None
1394 for f in (helpcmd, helptopic, helpext):
1389 for f in (helpcmd, helptopic, helpext):
1395 try:
1390 try:
1396 f(name)
1391 f(name)
1397 i = None
1392 i = None
1398 break
1393 break
1399 except UnknownCommand, inst:
1394 except UnknownCommand, inst:
1400 i = inst
1395 i = inst
1401 if i:
1396 if i:
1402 raise i
1397 raise i
1403
1398
1404 else:
1399 else:
1405 # program name
1400 # program name
1406 if ui.verbose or with_version:
1401 if ui.verbose or with_version:
1407 version_(ui)
1402 version_(ui)
1408 else:
1403 else:
1409 ui.status(_("Mercurial Distributed SCM\n"))
1404 ui.status(_("Mercurial Distributed SCM\n"))
1410 ui.status('\n')
1405 ui.status('\n')
1411
1406
1412 # list of commands
1407 # list of commands
1413 if name == "shortlist":
1408 if name == "shortlist":
1414 ui.status(_('basic commands:\n\n'))
1409 ui.status(_('basic commands:\n\n'))
1415 else:
1410 else:
1416 ui.status(_('list of commands:\n\n'))
1411 ui.status(_('list of commands:\n\n'))
1417
1412
1418 helplist()
1413 helplist()
1419
1414
1420 # list all option lists
1415 # list all option lists
1421 opt_output = []
1416 opt_output = []
1422 for title, options in option_lists:
1417 for title, options in option_lists:
1423 opt_output.append(("\n%s" % title, None))
1418 opt_output.append(("\n%s" % title, None))
1424 for shortopt, longopt, default, desc in options:
1419 for shortopt, longopt, default, desc in options:
1425 if "DEPRECATED" in desc and not ui.verbose: continue
1420 if "DEPRECATED" in desc and not ui.verbose: continue
1426 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1421 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1427 longopt and " --%s" % longopt),
1422 longopt and " --%s" % longopt),
1428 "%s%s" % (desc,
1423 "%s%s" % (desc,
1429 default
1424 default
1430 and _(" (default: %s)") % default
1425 and _(" (default: %s)") % default
1431 or "")))
1426 or "")))
1432
1427
1433 if opt_output:
1428 if opt_output:
1434 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1429 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1435 for first, second in opt_output:
1430 for first, second in opt_output:
1436 if second:
1431 if second:
1437 ui.write(" %-*s %s\n" % (opts_len, first, second))
1432 ui.write(" %-*s %s\n" % (opts_len, first, second))
1438 else:
1433 else:
1439 ui.write("%s\n" % first)
1434 ui.write("%s\n" % first)
1440
1435
1441 def identify(ui, repo):
1436 def identify(ui, repo):
1442 """print information about the working copy
1437 """print information about the working copy
1443
1438
1444 Print a short summary of the current state of the repo.
1439 Print a short summary of the current state of the repo.
1445
1440
1446 This summary identifies the repository state using one or two parent
1441 This summary identifies the repository state using one or two parent
1447 hash identifiers, followed by a "+" if there are uncommitted changes
1442 hash identifiers, followed by a "+" if there are uncommitted changes
1448 in the working directory, followed by a list of tags for this revision.
1443 in the working directory, followed by a list of tags for this revision.
1449 """
1444 """
1450 parents = [p for p in repo.dirstate.parents() if p != nullid]
1445 parents = [p for p in repo.dirstate.parents() if p != nullid]
1451 if not parents:
1446 if not parents:
1452 ui.write(_("unknown\n"))
1447 ui.write(_("unknown\n"))
1453 return
1448 return
1454
1449
1455 hexfunc = ui.debugflag and hex or short
1450 hexfunc = ui.debugflag and hex or short
1456 modified, added, removed, deleted = repo.status()[:4]
1451 modified, added, removed, deleted = repo.status()[:4]
1457 output = ["%s%s" %
1452 output = ["%s%s" %
1458 ('+'.join([hexfunc(parent) for parent in parents]),
1453 ('+'.join([hexfunc(parent) for parent in parents]),
1459 (modified or added or removed or deleted) and "+" or "")]
1454 (modified or added or removed or deleted) and "+" or "")]
1460
1455
1461 if not ui.quiet:
1456 if not ui.quiet:
1462
1457
1463 branch = util.tolocal(repo.workingctx().branch())
1458 branch = util.tolocal(repo.workingctx().branch())
1464 if branch != 'default':
1459 if branch != 'default':
1465 output.append("(%s)" % branch)
1460 output.append("(%s)" % branch)
1466
1461
1467 # multiple tags for a single parent separated by '/'
1462 # multiple tags for a single parent separated by '/'
1468 parenttags = ['/'.join(tags)
1463 parenttags = ['/'.join(tags)
1469 for tags in map(repo.nodetags, parents) if tags]
1464 for tags in map(repo.nodetags, parents) if tags]
1470 # tags for multiple parents separated by ' + '
1465 # tags for multiple parents separated by ' + '
1471 if parenttags:
1466 if parenttags:
1472 output.append(' + '.join(parenttags))
1467 output.append(' + '.join(parenttags))
1473
1468
1474 ui.write("%s\n" % ' '.join(output))
1469 ui.write("%s\n" % ' '.join(output))
1475
1470
1476 def import_(ui, repo, patch1, *patches, **opts):
1471 def import_(ui, repo, patch1, *patches, **opts):
1477 """import an ordered set of patches
1472 """import an ordered set of patches
1478
1473
1479 Import a list of patches and commit them individually.
1474 Import a list of patches and commit them individually.
1480
1475
1481 If there are outstanding changes in the working directory, import
1476 If there are outstanding changes in the working directory, import
1482 will abort unless given the -f flag.
1477 will abort unless given the -f flag.
1483
1478
1484 You can import a patch straight from a mail message. Even patches
1479 You can import a patch straight from a mail message. Even patches
1485 as attachments work (body part must be type text/plain or
1480 as attachments work (body part must be type text/plain or
1486 text/x-patch to be used). From and Subject headers of email
1481 text/x-patch to be used). From and Subject headers of email
1487 message are used as default committer and commit message. All
1482 message are used as default committer and commit message. All
1488 text/plain body parts before first diff are added to commit
1483 text/plain body parts before first diff are added to commit
1489 message.
1484 message.
1490
1485
1491 If the imported patch was generated by hg export, user and description
1486 If the imported patch was generated by hg export, user and description
1492 from patch override values from message headers and body. Values
1487 from patch override values from message headers and body. Values
1493 given on command line with -m and -u override these.
1488 given on command line with -m and -u override these.
1494
1489
1495 If --exact is specified, import will set the working directory
1490 If --exact is specified, import will set the working directory
1496 to the parent of each patch before applying it, and will abort
1491 to the parent of each patch before applying it, and will abort
1497 if the resulting changeset has a different ID than the one
1492 if the resulting changeset has a different ID than the one
1498 recorded in the patch. This may happen due to character set
1493 recorded in the patch. This may happen due to character set
1499 problems or other deficiencies in the text patch format.
1494 problems or other deficiencies in the text patch format.
1500
1495
1501 To read a patch from standard input, use patch name "-".
1496 To read a patch from standard input, use patch name "-".
1502 """
1497 """
1503 patches = (patch1,) + patches
1498 patches = (patch1,) + patches
1504
1499
1505 if opts.get('exact') or not opts['force']:
1500 if opts.get('exact') or not opts['force']:
1506 bail_if_changed(repo)
1501 bail_if_changed(repo)
1507
1502
1508 d = opts["base"]
1503 d = opts["base"]
1509 strip = opts["strip"]
1504 strip = opts["strip"]
1510
1505
1511 wlock = repo.wlock()
1506 wlock = repo.wlock()
1512 lock = repo.lock()
1507 lock = repo.lock()
1513
1508
1514 for p in patches:
1509 for p in patches:
1515 pf = os.path.join(d, p)
1510 pf = os.path.join(d, p)
1516
1511
1517 if pf == '-':
1512 if pf == '-':
1518 ui.status(_("applying patch from stdin\n"))
1513 ui.status(_("applying patch from stdin\n"))
1519 tmpname, message, user, date, nodeid, p1, p2 = patch.extract(ui, sys.stdin)
1514 tmpname, message, user, date, nodeid, p1, p2 = patch.extract(ui, sys.stdin)
1520 else:
1515 else:
1521 ui.status(_("applying %s\n") % p)
1516 ui.status(_("applying %s\n") % p)
1522 tmpname, message, user, date, nodeid, p1, p2 = patch.extract(ui, file(pf))
1517 tmpname, message, user, date, nodeid, p1, p2 = patch.extract(ui, file(pf))
1523
1518
1524 if tmpname is None:
1519 if tmpname is None:
1525 raise util.Abort(_('no diffs found'))
1520 raise util.Abort(_('no diffs found'))
1526
1521
1527 try:
1522 try:
1528 cmdline_message = logmessage(opts)
1523 cmdline_message = logmessage(opts)
1529 if cmdline_message:
1524 if cmdline_message:
1530 # pickup the cmdline msg
1525 # pickup the cmdline msg
1531 message = cmdline_message
1526 message = cmdline_message
1532 elif message:
1527 elif message:
1533 # pickup the patch msg
1528 # pickup the patch msg
1534 message = message.strip()
1529 message = message.strip()
1535 else:
1530 else:
1536 # launch the editor
1531 # launch the editor
1537 message = None
1532 message = None
1538 ui.debug(_('message:\n%s\n') % message)
1533 ui.debug(_('message:\n%s\n') % message)
1539
1534
1540 wp = repo.workingctx().parents()
1535 wp = repo.workingctx().parents()
1541 if opts.get('exact'):
1536 if opts.get('exact'):
1542 if not nodeid or not p1:
1537 if not nodeid or not p1:
1543 raise util.Abort(_('not a mercurial patch'))
1538 raise util.Abort(_('not a mercurial patch'))
1544 p1 = repo.lookup(p1)
1539 p1 = repo.lookup(p1)
1545 p2 = repo.lookup(p2 or hex(nullid))
1540 p2 = repo.lookup(p2 or hex(nullid))
1546
1541
1547 if p1 != wp[0].node():
1542 if p1 != wp[0].node():
1548 hg.clean(repo, p1, wlock=wlock)
1543 hg.clean(repo, p1, wlock=wlock)
1549 repo.dirstate.setparents(p1, p2)
1544 repo.dirstate.setparents(p1, p2)
1550 elif p2:
1545 elif p2:
1551 try:
1546 try:
1552 p1 = repo.lookup(p1)
1547 p1 = repo.lookup(p1)
1553 p2 = repo.lookup(p2)
1548 p2 = repo.lookup(p2)
1554 if p1 == wp[0].node():
1549 if p1 == wp[0].node():
1555 repo.dirstate.setparents(p1, p2)
1550 repo.dirstate.setparents(p1, p2)
1556 except hg.RepoError:
1551 except hg.RepoError:
1557 pass
1552 pass
1558
1553
1559 files = {}
1554 files = {}
1560 try:
1555 try:
1561 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1556 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1562 files=files)
1557 files=files)
1563 finally:
1558 finally:
1564 files = patch.updatedir(ui, repo, files, wlock=wlock)
1559 files = patch.updatedir(ui, repo, files, wlock=wlock)
1565 n = repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1560 n = repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1566 if opts.get('exact'):
1561 if opts.get('exact'):
1567 if hex(n) != nodeid:
1562 if hex(n) != nodeid:
1568 repo.rollback(wlock=wlock, lock=lock)
1563 repo.rollback(wlock=wlock, lock=lock)
1569 raise util.Abort(_('patch is damaged or loses information'))
1564 raise util.Abort(_('patch is damaged or loses information'))
1570 finally:
1565 finally:
1571 os.unlink(tmpname)
1566 os.unlink(tmpname)
1572
1567
1573 def incoming(ui, repo, source="default", **opts):
1568 def incoming(ui, repo, source="default", **opts):
1574 """show new changesets found in source
1569 """show new changesets found in source
1575
1570
1576 Show new changesets found in the specified path/URL or the default
1571 Show new changesets found in the specified path/URL or the default
1577 pull location. These are the changesets that would be pulled if a pull
1572 pull location. These are the changesets that would be pulled if a pull
1578 was requested.
1573 was requested.
1579
1574
1580 For remote repository, using --bundle avoids downloading the changesets
1575 For remote repository, using --bundle avoids downloading the changesets
1581 twice if the incoming is followed by a pull.
1576 twice if the incoming is followed by a pull.
1582
1577
1583 See pull for valid source format details.
1578 See pull for valid source format details.
1584 """
1579 """
1585 source = ui.expandpath(source)
1580 source = ui.expandpath(source)
1586 setremoteconfig(ui, opts)
1581 setremoteconfig(ui, opts)
1587
1582
1588 other = hg.repository(ui, source)
1583 other = hg.repository(ui, source)
1589 ui.status(_('comparing with %s\n') % source)
1584 ui.status(_('comparing with %s\n') % source)
1590 incoming = repo.findincoming(other, force=opts["force"])
1585 incoming = repo.findincoming(other, force=opts["force"])
1591 if not incoming:
1586 if not incoming:
1592 try:
1587 try:
1593 os.unlink(opts["bundle"])
1588 os.unlink(opts["bundle"])
1594 except:
1589 except:
1595 pass
1590 pass
1596 ui.status(_("no changes found\n"))
1591 ui.status(_("no changes found\n"))
1597 return 1
1592 return 1
1598
1593
1599 cleanup = None
1594 cleanup = None
1600 try:
1595 try:
1601 fname = opts["bundle"]
1596 fname = opts["bundle"]
1602 if fname or not other.local():
1597 if fname or not other.local():
1603 # create a bundle (uncompressed if other repo is not local)
1598 # create a bundle (uncompressed if other repo is not local)
1604 cg = other.changegroup(incoming, "incoming")
1599 cg = other.changegroup(incoming, "incoming")
1605 bundletype = other.local() and "HG10BZ" or "HG10UN"
1600 bundletype = other.local() and "HG10BZ" or "HG10UN"
1606 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1601 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1607 # keep written bundle?
1602 # keep written bundle?
1608 if opts["bundle"]:
1603 if opts["bundle"]:
1609 cleanup = None
1604 cleanup = None
1610 if not other.local():
1605 if not other.local():
1611 # use the created uncompressed bundlerepo
1606 # use the created uncompressed bundlerepo
1612 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1607 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1613
1608
1614 revs = None
1609 revs = None
1615 if opts['rev']:
1610 if opts['rev']:
1616 revs = [other.lookup(rev) for rev in opts['rev']]
1611 revs = [other.lookup(rev) for rev in opts['rev']]
1617 o = other.changelog.nodesbetween(incoming, revs)[0]
1612 o = other.changelog.nodesbetween(incoming, revs)[0]
1618 if opts['newest_first']:
1613 if opts['newest_first']:
1619 o.reverse()
1614 o.reverse()
1620 displayer = cmdutil.show_changeset(ui, other, opts)
1615 displayer = cmdutil.show_changeset(ui, other, opts)
1621 for n in o:
1616 for n in o:
1622 parents = [p for p in other.changelog.parents(n) if p != nullid]
1617 parents = [p for p in other.changelog.parents(n) if p != nullid]
1623 if opts['no_merges'] and len(parents) == 2:
1618 if opts['no_merges'] and len(parents) == 2:
1624 continue
1619 continue
1625 displayer.show(changenode=n)
1620 displayer.show(changenode=n)
1626 finally:
1621 finally:
1627 if hasattr(other, 'close'):
1622 if hasattr(other, 'close'):
1628 other.close()
1623 other.close()
1629 if cleanup:
1624 if cleanup:
1630 os.unlink(cleanup)
1625 os.unlink(cleanup)
1631
1626
1632 def init(ui, dest=".", **opts):
1627 def init(ui, dest=".", **opts):
1633 """create a new repository in the given directory
1628 """create a new repository in the given directory
1634
1629
1635 Initialize a new repository in the given directory. If the given
1630 Initialize a new repository in the given directory. If the given
1636 directory does not exist, it is created.
1631 directory does not exist, it is created.
1637
1632
1638 If no directory is given, the current directory is used.
1633 If no directory is given, the current directory is used.
1639
1634
1640 It is possible to specify an ssh:// URL as the destination.
1635 It is possible to specify an ssh:// URL as the destination.
1641 Look at the help text for the pull command for important details
1636 Look at the help text for the pull command for important details
1642 about ssh:// URLs.
1637 about ssh:// URLs.
1643 """
1638 """
1644 setremoteconfig(ui, opts)
1639 setremoteconfig(ui, opts)
1645 hg.repository(ui, dest, create=1)
1640 hg.repository(ui, dest, create=1)
1646
1641
1647 def locate(ui, repo, *pats, **opts):
1642 def locate(ui, repo, *pats, **opts):
1648 """locate files matching specific patterns
1643 """locate files matching specific patterns
1649
1644
1650 Print all files under Mercurial control whose names match the
1645 Print all files under Mercurial control whose names match the
1651 given patterns.
1646 given patterns.
1652
1647
1653 This command searches the entire repository by default. To search
1648 This command searches the entire repository by default. To search
1654 just the current directory and its subdirectories, use "--include .".
1649 just the current directory and its subdirectories, use "--include .".
1655
1650
1656 If no patterns are given to match, this command prints all file
1651 If no patterns are given to match, this command prints all file
1657 names.
1652 names.
1658
1653
1659 If you want to feed the output of this command into the "xargs"
1654 If you want to feed the output of this command into the "xargs"
1660 command, use the "-0" option to both this command and "xargs".
1655 command, use the "-0" option to both this command and "xargs".
1661 This will avoid the problem of "xargs" treating single filenames
1656 This will avoid the problem of "xargs" treating single filenames
1662 that contain white space as multiple filenames.
1657 that contain white space as multiple filenames.
1663 """
1658 """
1664 end = opts['print0'] and '\0' or '\n'
1659 end = opts['print0'] and '\0' or '\n'
1665 rev = opts['rev']
1660 rev = opts['rev']
1666 if rev:
1661 if rev:
1667 node = repo.lookup(rev)
1662 node = repo.lookup(rev)
1668 else:
1663 else:
1669 node = None
1664 node = None
1670
1665
1671 ret = 1
1666 ret = 1
1672 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1667 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1673 badmatch=util.always,
1668 badmatch=util.always,
1674 default='relglob'):
1669 default='relglob'):
1675 if src == 'b':
1670 if src == 'b':
1676 continue
1671 continue
1677 if not node and repo.dirstate.state(abs) == '?':
1672 if not node and repo.dirstate.state(abs) == '?':
1678 continue
1673 continue
1679 if opts['fullpath']:
1674 if opts['fullpath']:
1680 ui.write(os.path.join(repo.root, abs), end)
1675 ui.write(os.path.join(repo.root, abs), end)
1681 else:
1676 else:
1682 ui.write(((pats and rel) or abs), end)
1677 ui.write(((pats and rel) or abs), end)
1683 ret = 0
1678 ret = 0
1684
1679
1685 return ret
1680 return ret
1686
1681
1687 def log(ui, repo, *pats, **opts):
1682 def log(ui, repo, *pats, **opts):
1688 """show revision history of entire repository or files
1683 """show revision history of entire repository or files
1689
1684
1690 Print the revision history of the specified files or the entire
1685 Print the revision history of the specified files or the entire
1691 project.
1686 project.
1692
1687
1693 File history is shown without following rename or copy history of
1688 File history is shown without following rename or copy history of
1694 files. Use -f/--follow with a file name to follow history across
1689 files. Use -f/--follow with a file name to follow history across
1695 renames and copies. --follow without a file name will only show
1690 renames and copies. --follow without a file name will only show
1696 ancestors or descendants of the starting revision. --follow-first
1691 ancestors or descendants of the starting revision. --follow-first
1697 only follows the first parent of merge revisions.
1692 only follows the first parent of merge revisions.
1698
1693
1699 If no revision range is specified, the default is tip:0 unless
1694 If no revision range is specified, the default is tip:0 unless
1700 --follow is set, in which case the working directory parent is
1695 --follow is set, in which case the working directory parent is
1701 used as the starting revision.
1696 used as the starting revision.
1702
1697
1703 By default this command outputs: changeset id and hash, tags,
1698 By default this command outputs: changeset id and hash, tags,
1704 non-trivial parents, user, date and time, and a summary for each
1699 non-trivial parents, user, date and time, and a summary for each
1705 commit. When the -v/--verbose switch is used, the list of changed
1700 commit. When the -v/--verbose switch is used, the list of changed
1706 files and full commit message is shown.
1701 files and full commit message is shown.
1707
1702
1708 NOTE: log -p may generate unexpected diff output for merge
1703 NOTE: log -p may generate unexpected diff output for merge
1709 changesets, as it will compare the merge changeset against its
1704 changesets, as it will compare the merge changeset against its
1710 first parent only. Also, the files: list will only reflect files
1705 first parent only. Also, the files: list will only reflect files
1711 that are different from BOTH parents.
1706 that are different from BOTH parents.
1712
1707
1713 """
1708 """
1714
1709
1715 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1710 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1716 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1711 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1717
1712
1718 if opts['limit']:
1713 if opts['limit']:
1719 try:
1714 try:
1720 limit = int(opts['limit'])
1715 limit = int(opts['limit'])
1721 except ValueError:
1716 except ValueError:
1722 raise util.Abort(_('limit must be a positive integer'))
1717 raise util.Abort(_('limit must be a positive integer'))
1723 if limit <= 0: raise util.Abort(_('limit must be positive'))
1718 if limit <= 0: raise util.Abort(_('limit must be positive'))
1724 else:
1719 else:
1725 limit = sys.maxint
1720 limit = sys.maxint
1726 count = 0
1721 count = 0
1727
1722
1728 if opts['copies'] and opts['rev']:
1723 if opts['copies'] and opts['rev']:
1729 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1724 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1730 else:
1725 else:
1731 endrev = repo.changelog.count()
1726 endrev = repo.changelog.count()
1732 rcache = {}
1727 rcache = {}
1733 ncache = {}
1728 ncache = {}
1734 dcache = []
1729 dcache = []
1735 def getrenamed(fn, rev, man):
1730 def getrenamed(fn, rev, man):
1736 '''looks up all renames for a file (up to endrev) the first
1731 '''looks up all renames for a file (up to endrev) the first
1737 time the file is given. It indexes on the changerev and only
1732 time the file is given. It indexes on the changerev and only
1738 parses the manifest if linkrev != changerev.
1733 parses the manifest if linkrev != changerev.
1739 Returns rename info for fn at changerev rev.'''
1734 Returns rename info for fn at changerev rev.'''
1740 if fn not in rcache:
1735 if fn not in rcache:
1741 rcache[fn] = {}
1736 rcache[fn] = {}
1742 ncache[fn] = {}
1737 ncache[fn] = {}
1743 fl = repo.file(fn)
1738 fl = repo.file(fn)
1744 for i in xrange(fl.count()):
1739 for i in xrange(fl.count()):
1745 node = fl.node(i)
1740 node = fl.node(i)
1746 lr = fl.linkrev(node)
1741 lr = fl.linkrev(node)
1747 renamed = fl.renamed(node)
1742 renamed = fl.renamed(node)
1748 rcache[fn][lr] = renamed
1743 rcache[fn][lr] = renamed
1749 if renamed:
1744 if renamed:
1750 ncache[fn][node] = renamed
1745 ncache[fn][node] = renamed
1751 if lr >= endrev:
1746 if lr >= endrev:
1752 break
1747 break
1753 if rev in rcache[fn]:
1748 if rev in rcache[fn]:
1754 return rcache[fn][rev]
1749 return rcache[fn][rev]
1755 mr = repo.manifest.rev(man)
1750 mr = repo.manifest.rev(man)
1756 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1751 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1757 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1752 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1758 if not dcache or dcache[0] != man:
1753 if not dcache or dcache[0] != man:
1759 dcache[:] = [man, repo.manifest.readdelta(man)]
1754 dcache[:] = [man, repo.manifest.readdelta(man)]
1760 if fn in dcache[1]:
1755 if fn in dcache[1]:
1761 return ncache[fn].get(dcache[1][fn])
1756 return ncache[fn].get(dcache[1][fn])
1762 return None
1757 return None
1763
1758
1764 df = False
1759 df = False
1765 if opts["date"]:
1760 if opts["date"]:
1766 df = util.matchdate(opts["date"])
1761 df = util.matchdate(opts["date"])
1767
1762
1768 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1763 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1769 for st, rev, fns in changeiter:
1764 for st, rev, fns in changeiter:
1770 if st == 'add':
1765 if st == 'add':
1771 changenode = repo.changelog.node(rev)
1766 changenode = repo.changelog.node(rev)
1772 parents = [p for p in repo.changelog.parentrevs(rev)
1767 parents = [p for p in repo.changelog.parentrevs(rev)
1773 if p != nullrev]
1768 if p != nullrev]
1774 if opts['no_merges'] and len(parents) == 2:
1769 if opts['no_merges'] and len(parents) == 2:
1775 continue
1770 continue
1776 if opts['only_merges'] and len(parents) != 2:
1771 if opts['only_merges'] and len(parents) != 2:
1777 continue
1772 continue
1778
1773
1779 if df:
1774 if df:
1780 changes = get(rev)
1775 changes = get(rev)
1781 if not df(changes[2][0]):
1776 if not df(changes[2][0]):
1782 continue
1777 continue
1783
1778
1784 if opts['keyword']:
1779 if opts['keyword']:
1785 changes = get(rev)
1780 changes = get(rev)
1786 miss = 0
1781 miss = 0
1787 for k in [kw.lower() for kw in opts['keyword']]:
1782 for k in [kw.lower() for kw in opts['keyword']]:
1788 if not (k in changes[1].lower() or
1783 if not (k in changes[1].lower() or
1789 k in changes[4].lower() or
1784 k in changes[4].lower() or
1790 k in " ".join(changes[3]).lower()):
1785 k in " ".join(changes[3]).lower()):
1791 miss = 1
1786 miss = 1
1792 break
1787 break
1793 if miss:
1788 if miss:
1794 continue
1789 continue
1795
1790
1796 copies = []
1791 copies = []
1797 if opts.get('copies') and rev:
1792 if opts.get('copies') and rev:
1798 mf = get(rev)[0]
1793 mf = get(rev)[0]
1799 for fn in get(rev)[3]:
1794 for fn in get(rev)[3]:
1800 rename = getrenamed(fn, rev, mf)
1795 rename = getrenamed(fn, rev, mf)
1801 if rename:
1796 if rename:
1802 copies.append((fn, rename[0]))
1797 copies.append((fn, rename[0]))
1803 displayer.show(rev, changenode, copies=copies)
1798 displayer.show(rev, changenode, copies=copies)
1804 elif st == 'iter':
1799 elif st == 'iter':
1805 if count == limit: break
1800 if count == limit: break
1806 if displayer.flush(rev):
1801 if displayer.flush(rev):
1807 count += 1
1802 count += 1
1808
1803
1809 def manifest(ui, repo, rev=None):
1804 def manifest(ui, repo, rev=None):
1810 """output the current or given revision of the project manifest
1805 """output the current or given revision of the project manifest
1811
1806
1812 Print a list of version controlled files for the given revision.
1807 Print a list of version controlled files for the given revision.
1813 If no revision is given, the parent of the working directory is used,
1808 If no revision is given, the parent of the working directory is used,
1814 or tip if no revision is checked out.
1809 or tip if no revision is checked out.
1815
1810
1816 The manifest is the list of files being version controlled. If no revision
1811 The manifest is the list of files being version controlled. If no revision
1817 is given then the first parent of the working directory is used.
1812 is given then the first parent of the working directory is used.
1818
1813
1819 With -v flag, print file permissions. With --debug flag, print
1814 With -v flag, print file permissions. With --debug flag, print
1820 file revision hashes.
1815 file revision hashes.
1821 """
1816 """
1822
1817
1823 m = repo.changectx(rev).manifest()
1818 m = repo.changectx(rev).manifest()
1824 files = m.keys()
1819 files = m.keys()
1825 files.sort()
1820 files.sort()
1826
1821
1827 for f in files:
1822 for f in files:
1828 if ui.debugflag:
1823 if ui.debugflag:
1829 ui.write("%40s " % hex(m[f]))
1824 ui.write("%40s " % hex(m[f]))
1830 if ui.verbose:
1825 if ui.verbose:
1831 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1826 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1832 ui.write("%s\n" % f)
1827 ui.write("%s\n" % f)
1833
1828
1834 def merge(ui, repo, node=None, force=None):
1829 def merge(ui, repo, node=None, force=None):
1835 """merge working directory with another revision
1830 """merge working directory with another revision
1836
1831
1837 Merge the contents of the current working directory and the
1832 Merge the contents of the current working directory and the
1838 requested revision. Files that changed between either parent are
1833 requested revision. Files that changed between either parent are
1839 marked as changed for the next commit and a commit must be
1834 marked as changed for the next commit and a commit must be
1840 performed before any further updates are allowed.
1835 performed before any further updates are allowed.
1841
1836
1842 If no revision is specified, the working directory's parent is a
1837 If no revision is specified, the working directory's parent is a
1843 head revision, and the repository contains exactly one other head,
1838 head revision, and the repository contains exactly one other head,
1844 the other head is merged with by default. Otherwise, an explicit
1839 the other head is merged with by default. Otherwise, an explicit
1845 revision to merge with must be provided.
1840 revision to merge with must be provided.
1846 """
1841 """
1847
1842
1848 if not node:
1843 if not node:
1849 heads = repo.heads()
1844 heads = repo.heads()
1850 if len(heads) > 2:
1845 if len(heads) > 2:
1851 raise util.Abort(_('repo has %d heads - '
1846 raise util.Abort(_('repo has %d heads - '
1852 'please merge with an explicit rev') %
1847 'please merge with an explicit rev') %
1853 len(heads))
1848 len(heads))
1854 if len(heads) == 1:
1849 if len(heads) == 1:
1855 raise util.Abort(_('there is nothing to merge - '
1850 raise util.Abort(_('there is nothing to merge - '
1856 'use "hg update" instead'))
1851 'use "hg update" instead'))
1857 parent = repo.dirstate.parents()[0]
1852 parent = repo.dirstate.parents()[0]
1858 if parent not in heads:
1853 if parent not in heads:
1859 raise util.Abort(_('working dir not at a head rev - '
1854 raise util.Abort(_('working dir not at a head rev - '
1860 'use "hg update" or merge with an explicit rev'))
1855 'use "hg update" or merge with an explicit rev'))
1861 node = parent == heads[0] and heads[-1] or heads[0]
1856 node = parent == heads[0] and heads[-1] or heads[0]
1862 return hg.merge(repo, node, force=force)
1857 return hg.merge(repo, node, force=force)
1863
1858
1864 def outgoing(ui, repo, dest=None, **opts):
1859 def outgoing(ui, repo, dest=None, **opts):
1865 """show changesets not found in destination
1860 """show changesets not found in destination
1866
1861
1867 Show changesets not found in the specified destination repository or
1862 Show changesets not found in the specified destination repository or
1868 the default push location. These are the changesets that would be pushed
1863 the default push location. These are the changesets that would be pushed
1869 if a push was requested.
1864 if a push was requested.
1870
1865
1871 See pull for valid destination format details.
1866 See pull for valid destination format details.
1872 """
1867 """
1873 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1868 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1874 setremoteconfig(ui, opts)
1869 setremoteconfig(ui, opts)
1875 revs = None
1870 revs = None
1876 if opts['rev']:
1871 if opts['rev']:
1877 revs = [repo.lookup(rev) for rev in opts['rev']]
1872 revs = [repo.lookup(rev) for rev in opts['rev']]
1878
1873
1879 other = hg.repository(ui, dest)
1874 other = hg.repository(ui, dest)
1880 ui.status(_('comparing with %s\n') % dest)
1875 ui.status(_('comparing with %s\n') % dest)
1881 o = repo.findoutgoing(other, force=opts['force'])
1876 o = repo.findoutgoing(other, force=opts['force'])
1882 if not o:
1877 if not o:
1883 ui.status(_("no changes found\n"))
1878 ui.status(_("no changes found\n"))
1884 return 1
1879 return 1
1885 o = repo.changelog.nodesbetween(o, revs)[0]
1880 o = repo.changelog.nodesbetween(o, revs)[0]
1886 if opts['newest_first']:
1881 if opts['newest_first']:
1887 o.reverse()
1882 o.reverse()
1888 displayer = cmdutil.show_changeset(ui, repo, opts)
1883 displayer = cmdutil.show_changeset(ui, repo, opts)
1889 for n in o:
1884 for n in o:
1890 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1885 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1891 if opts['no_merges'] and len(parents) == 2:
1886 if opts['no_merges'] and len(parents) == 2:
1892 continue
1887 continue
1893 displayer.show(changenode=n)
1888 displayer.show(changenode=n)
1894
1889
1895 def parents(ui, repo, file_=None, **opts):
1890 def parents(ui, repo, file_=None, **opts):
1896 """show the parents of the working dir or revision
1891 """show the parents of the working dir or revision
1897
1892
1898 Print the working directory's parent revisions.
1893 Print the working directory's parent revisions.
1899 """
1894 """
1900 rev = opts.get('rev')
1895 rev = opts.get('rev')
1901 if rev:
1896 if rev:
1902 if file_:
1897 if file_:
1903 ctx = repo.filectx(file_, changeid=rev)
1898 ctx = repo.filectx(file_, changeid=rev)
1904 else:
1899 else:
1905 ctx = repo.changectx(rev)
1900 ctx = repo.changectx(rev)
1906 p = [cp.node() for cp in ctx.parents()]
1901 p = [cp.node() for cp in ctx.parents()]
1907 else:
1902 else:
1908 p = repo.dirstate.parents()
1903 p = repo.dirstate.parents()
1909
1904
1910 displayer = cmdutil.show_changeset(ui, repo, opts)
1905 displayer = cmdutil.show_changeset(ui, repo, opts)
1911 for n in p:
1906 for n in p:
1912 if n != nullid:
1907 if n != nullid:
1913 displayer.show(changenode=n)
1908 displayer.show(changenode=n)
1914
1909
1915 def paths(ui, repo, search=None):
1910 def paths(ui, repo, search=None):
1916 """show definition of symbolic path names
1911 """show definition of symbolic path names
1917
1912
1918 Show definition of symbolic path name NAME. If no name is given, show
1913 Show definition of symbolic path name NAME. If no name is given, show
1919 definition of available names.
1914 definition of available names.
1920
1915
1921 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1916 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1922 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1917 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1923 """
1918 """
1924 if search:
1919 if search:
1925 for name, path in ui.configitems("paths"):
1920 for name, path in ui.configitems("paths"):
1926 if name == search:
1921 if name == search:
1927 ui.write("%s\n" % path)
1922 ui.write("%s\n" % path)
1928 return
1923 return
1929 ui.warn(_("not found!\n"))
1924 ui.warn(_("not found!\n"))
1930 return 1
1925 return 1
1931 else:
1926 else:
1932 for name, path in ui.configitems("paths"):
1927 for name, path in ui.configitems("paths"):
1933 ui.write("%s = %s\n" % (name, path))
1928 ui.write("%s = %s\n" % (name, path))
1934
1929
1935 def postincoming(ui, repo, modheads, optupdate):
1930 def postincoming(ui, repo, modheads, optupdate):
1936 if modheads == 0:
1931 if modheads == 0:
1937 return
1932 return
1938 if optupdate:
1933 if optupdate:
1939 if modheads == 1:
1934 if modheads == 1:
1940 return hg.update(repo, repo.changelog.tip()) # update
1935 return hg.update(repo, repo.changelog.tip()) # update
1941 else:
1936 else:
1942 ui.status(_("not updating, since new heads added\n"))
1937 ui.status(_("not updating, since new heads added\n"))
1943 if modheads > 1:
1938 if modheads > 1:
1944 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1939 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1945 else:
1940 else:
1946 ui.status(_("(run 'hg update' to get a working copy)\n"))
1941 ui.status(_("(run 'hg update' to get a working copy)\n"))
1947
1942
1948 def pull(ui, repo, source="default", **opts):
1943 def pull(ui, repo, source="default", **opts):
1949 """pull changes from the specified source
1944 """pull changes from the specified source
1950
1945
1951 Pull changes from a remote repository to a local one.
1946 Pull changes from a remote repository to a local one.
1952
1947
1953 This finds all changes from the repository at the specified path
1948 This finds all changes from the repository at the specified path
1954 or URL and adds them to the local repository. By default, this
1949 or URL and adds them to the local repository. By default, this
1955 does not update the copy of the project in the working directory.
1950 does not update the copy of the project in the working directory.
1956
1951
1957 Valid URLs are of the form:
1952 Valid URLs are of the form:
1958
1953
1959 local/filesystem/path (or file://local/filesystem/path)
1954 local/filesystem/path (or file://local/filesystem/path)
1960 http://[user@]host[:port]/[path]
1955 http://[user@]host[:port]/[path]
1961 https://[user@]host[:port]/[path]
1956 https://[user@]host[:port]/[path]
1962 ssh://[user@]host[:port]/[path]
1957 ssh://[user@]host[:port]/[path]
1963 static-http://host[:port]/[path]
1958 static-http://host[:port]/[path]
1964
1959
1965 Paths in the local filesystem can either point to Mercurial
1960 Paths in the local filesystem can either point to Mercurial
1966 repositories or to bundle files (as created by 'hg bundle' or
1961 repositories or to bundle files (as created by 'hg bundle' or
1967 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1962 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
1968 allows access to a Mercurial repository where you simply use a web
1963 allows access to a Mercurial repository where you simply use a web
1969 server to publish the .hg directory as static content.
1964 server to publish the .hg directory as static content.
1970
1965
1971 Some notes about using SSH with Mercurial:
1966 Some notes about using SSH with Mercurial:
1972 - SSH requires an accessible shell account on the destination machine
1967 - SSH requires an accessible shell account on the destination machine
1973 and a copy of hg in the remote path or specified with as remotecmd.
1968 and a copy of hg in the remote path or specified with as remotecmd.
1974 - path is relative to the remote user's home directory by default.
1969 - path is relative to the remote user's home directory by default.
1975 Use an extra slash at the start of a path to specify an absolute path:
1970 Use an extra slash at the start of a path to specify an absolute path:
1976 ssh://example.com//tmp/repository
1971 ssh://example.com//tmp/repository
1977 - Mercurial doesn't use its own compression via SSH; the right thing
1972 - Mercurial doesn't use its own compression via SSH; the right thing
1978 to do is to configure it in your ~/.ssh/config, e.g.:
1973 to do is to configure it in your ~/.ssh/config, e.g.:
1979 Host *.mylocalnetwork.example.com
1974 Host *.mylocalnetwork.example.com
1980 Compression no
1975 Compression no
1981 Host *
1976 Host *
1982 Compression yes
1977 Compression yes
1983 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1978 Alternatively specify "ssh -C" as your ssh command in your hgrc or
1984 with the --ssh command line option.
1979 with the --ssh command line option.
1985 """
1980 """
1986 source = ui.expandpath(source)
1981 source = ui.expandpath(source)
1987 setremoteconfig(ui, opts)
1982 setremoteconfig(ui, opts)
1988
1983
1989 other = hg.repository(ui, source)
1984 other = hg.repository(ui, source)
1990 ui.status(_('pulling from %s\n') % (source))
1985 ui.status(_('pulling from %s\n') % (source))
1991 revs = None
1986 revs = None
1992 if opts['rev']:
1987 if opts['rev']:
1993 if 'lookup' in other.capabilities:
1988 if 'lookup' in other.capabilities:
1994 revs = [other.lookup(rev) for rev in opts['rev']]
1989 revs = [other.lookup(rev) for rev in opts['rev']]
1995 else:
1990 else:
1996 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1991 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1997 raise util.Abort(error)
1992 raise util.Abort(error)
1998 modheads = repo.pull(other, heads=revs, force=opts['force'])
1993 modheads = repo.pull(other, heads=revs, force=opts['force'])
1999 return postincoming(ui, repo, modheads, opts['update'])
1994 return postincoming(ui, repo, modheads, opts['update'])
2000
1995
2001 def push(ui, repo, dest=None, **opts):
1996 def push(ui, repo, dest=None, **opts):
2002 """push changes to the specified destination
1997 """push changes to the specified destination
2003
1998
2004 Push changes from the local repository to the given destination.
1999 Push changes from the local repository to the given destination.
2005
2000
2006 This is the symmetrical operation for pull. It helps to move
2001 This is the symmetrical operation for pull. It helps to move
2007 changes from the current repository to a different one. If the
2002 changes from the current repository to a different one. If the
2008 destination is local this is identical to a pull in that directory
2003 destination is local this is identical to a pull in that directory
2009 from the current one.
2004 from the current one.
2010
2005
2011 By default, push will refuse to run if it detects the result would
2006 By default, push will refuse to run if it detects the result would
2012 increase the number of remote heads. This generally indicates the
2007 increase the number of remote heads. This generally indicates the
2013 the client has forgotten to sync and merge before pushing.
2008 the client has forgotten to sync and merge before pushing.
2014
2009
2015 Valid URLs are of the form:
2010 Valid URLs are of the form:
2016
2011
2017 local/filesystem/path (or file://local/filesystem/path)
2012 local/filesystem/path (or file://local/filesystem/path)
2018 ssh://[user@]host[:port]/[path]
2013 ssh://[user@]host[:port]/[path]
2019 http://[user@]host[:port]/[path]
2014 http://[user@]host[:port]/[path]
2020 https://[user@]host[:port]/[path]
2015 https://[user@]host[:port]/[path]
2021
2016
2022 Look at the help text for the pull command for important details
2017 Look at the help text for the pull command for important details
2023 about ssh:// URLs.
2018 about ssh:// URLs.
2024
2019
2025 Pushing to http:// and https:// URLs is only possible, if this
2020 Pushing to http:// and https:// URLs is only possible, if this
2026 feature is explicitly enabled on the remote Mercurial server.
2021 feature is explicitly enabled on the remote Mercurial server.
2027 """
2022 """
2028 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2023 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2029 setremoteconfig(ui, opts)
2024 setremoteconfig(ui, opts)
2030
2025
2031 other = hg.repository(ui, dest)
2026 other = hg.repository(ui, dest)
2032 ui.status('pushing to %s\n' % (dest))
2027 ui.status('pushing to %s\n' % (dest))
2033 revs = None
2028 revs = None
2034 if opts['rev']:
2029 if opts['rev']:
2035 revs = [repo.lookup(rev) for rev in opts['rev']]
2030 revs = [repo.lookup(rev) for rev in opts['rev']]
2036 r = repo.push(other, opts['force'], revs=revs)
2031 r = repo.push(other, opts['force'], revs=revs)
2037 return r == 0
2032 return r == 0
2038
2033
2039 def rawcommit(ui, repo, *pats, **opts):
2034 def rawcommit(ui, repo, *pats, **opts):
2040 """raw commit interface (DEPRECATED)
2035 """raw commit interface (DEPRECATED)
2041
2036
2042 (DEPRECATED)
2037 (DEPRECATED)
2043 Lowlevel commit, for use in helper scripts.
2038 Lowlevel commit, for use in helper scripts.
2044
2039
2045 This command is not intended to be used by normal users, as it is
2040 This command is not intended to be used by normal users, as it is
2046 primarily useful for importing from other SCMs.
2041 primarily useful for importing from other SCMs.
2047
2042
2048 This command is now deprecated and will be removed in a future
2043 This command is now deprecated and will be removed in a future
2049 release, please use debugsetparents and commit instead.
2044 release, please use debugsetparents and commit instead.
2050 """
2045 """
2051
2046
2052 ui.warn(_("(the rawcommit command is deprecated)\n"))
2047 ui.warn(_("(the rawcommit command is deprecated)\n"))
2053
2048
2054 message = logmessage(opts)
2049 message = logmessage(opts)
2055
2050
2056 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2051 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2057 if opts['files']:
2052 if opts['files']:
2058 files += open(opts['files']).read().splitlines()
2053 files += open(opts['files']).read().splitlines()
2059
2054
2060 parents = [repo.lookup(p) for p in opts['parent']]
2055 parents = [repo.lookup(p) for p in opts['parent']]
2061
2056
2062 try:
2057 try:
2063 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2058 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2064 except ValueError, inst:
2059 except ValueError, inst:
2065 raise util.Abort(str(inst))
2060 raise util.Abort(str(inst))
2066
2061
2067 def recover(ui, repo):
2062 def recover(ui, repo):
2068 """roll back an interrupted transaction
2063 """roll back an interrupted transaction
2069
2064
2070 Recover from an interrupted commit or pull.
2065 Recover from an interrupted commit or pull.
2071
2066
2072 This command tries to fix the repository status after an interrupted
2067 This command tries to fix the repository status after an interrupted
2073 operation. It should only be necessary when Mercurial suggests it.
2068 operation. It should only be necessary when Mercurial suggests it.
2074 """
2069 """
2075 if repo.recover():
2070 if repo.recover():
2076 return hg.verify(repo)
2071 return hg.verify(repo)
2077 return 1
2072 return 1
2078
2073
2079 def remove(ui, repo, *pats, **opts):
2074 def remove(ui, repo, *pats, **opts):
2080 """remove the specified files on the next commit
2075 """remove the specified files on the next commit
2081
2076
2082 Schedule the indicated files for removal from the repository.
2077 Schedule the indicated files for removal from the repository.
2083
2078
2084 This only removes files from the current branch, not from the
2079 This only removes files from the current branch, not from the
2085 entire project history. If the files still exist in the working
2080 entire project history. If the files still exist in the working
2086 directory, they will be deleted from it. If invoked with --after,
2081 directory, they will be deleted from it. If invoked with --after,
2087 files are marked as removed, but not actually unlinked unless --force
2082 files are marked as removed, but not actually unlinked unless --force
2088 is also given. Without exact file names, --after will only mark
2083 is also given. Without exact file names, --after will only mark
2089 files as removed if they are no longer in the working directory.
2084 files as removed if they are no longer in the working directory.
2090
2085
2091 This command schedules the files to be removed at the next commit.
2086 This command schedules the files to be removed at the next commit.
2092 To undo a remove before that, see hg revert.
2087 To undo a remove before that, see hg revert.
2093
2088
2094 Modified files and added files are not removed by default. To
2089 Modified files and added files are not removed by default. To
2095 remove them, use the -f/--force option.
2090 remove them, use the -f/--force option.
2096 """
2091 """
2097 names = []
2092 names = []
2098 if not opts['after'] and not pats:
2093 if not opts['after'] and not pats:
2099 raise util.Abort(_('no files specified'))
2094 raise util.Abort(_('no files specified'))
2100 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2095 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2101 exact = dict.fromkeys(files)
2096 exact = dict.fromkeys(files)
2102 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2097 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2103 modified, added, removed, deleted, unknown = mardu
2098 modified, added, removed, deleted, unknown = mardu
2104 remove, forget = [], []
2099 remove, forget = [], []
2105 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2100 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2106 reason = None
2101 reason = None
2107 if abs in modified and not opts['force']:
2102 if abs in modified and not opts['force']:
2108 reason = _('is modified (use -f to force removal)')
2103 reason = _('is modified (use -f to force removal)')
2109 elif abs in added:
2104 elif abs in added:
2110 if opts['force']:
2105 if opts['force']:
2111 forget.append(abs)
2106 forget.append(abs)
2112 continue
2107 continue
2113 reason = _('has been marked for add (use -f to force removal)')
2108 reason = _('has been marked for add (use -f to force removal)')
2114 elif abs in unknown:
2109 elif abs in unknown:
2115 reason = _('is not managed')
2110 reason = _('is not managed')
2116 elif opts['after'] and not exact and abs not in deleted:
2111 elif opts['after'] and not exact and abs not in deleted:
2117 continue
2112 continue
2118 elif abs in removed:
2113 elif abs in removed:
2119 continue
2114 continue
2120 if reason:
2115 if reason:
2121 if exact:
2116 if exact:
2122 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2117 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2123 else:
2118 else:
2124 if ui.verbose or not exact:
2119 if ui.verbose or not exact:
2125 ui.status(_('removing %s\n') % rel)
2120 ui.status(_('removing %s\n') % rel)
2126 remove.append(abs)
2121 remove.append(abs)
2127 repo.forget(forget)
2122 repo.forget(forget)
2128 repo.remove(remove, unlink=opts['force'] or not opts['after'])
2123 repo.remove(remove, unlink=opts['force'] or not opts['after'])
2129
2124
2130 def rename(ui, repo, *pats, **opts):
2125 def rename(ui, repo, *pats, **opts):
2131 """rename files; equivalent of copy + remove
2126 """rename files; equivalent of copy + remove
2132
2127
2133 Mark dest as copies of sources; mark sources for deletion. If
2128 Mark dest as copies of sources; mark sources for deletion. If
2134 dest is a directory, copies are put in that directory. If dest is
2129 dest is a directory, copies are put in that directory. If dest is
2135 a file, there can only be one source.
2130 a file, there can only be one source.
2136
2131
2137 By default, this command copies the contents of files as they
2132 By default, this command copies the contents of files as they
2138 stand in the working directory. If invoked with --after, the
2133 stand in the working directory. If invoked with --after, the
2139 operation is recorded, but no copying is performed.
2134 operation is recorded, but no copying is performed.
2140
2135
2141 This command takes effect in the next commit. To undo a rename
2136 This command takes effect in the next commit. To undo a rename
2142 before that, see hg revert.
2137 before that, see hg revert.
2143 """
2138 """
2144 wlock = repo.wlock(0)
2139 wlock = repo.wlock(0)
2145 errs, copied = docopy(ui, repo, pats, opts, wlock)
2140 errs, copied = docopy(ui, repo, pats, opts, wlock)
2146 names = []
2141 names = []
2147 for abs, rel, exact in copied:
2142 for abs, rel, exact in copied:
2148 if ui.verbose or not exact:
2143 if ui.verbose or not exact:
2149 ui.status(_('removing %s\n') % rel)
2144 ui.status(_('removing %s\n') % rel)
2150 names.append(abs)
2145 names.append(abs)
2151 if not opts.get('dry_run'):
2146 if not opts.get('dry_run'):
2152 repo.remove(names, True, wlock=wlock)
2147 repo.remove(names, True, wlock=wlock)
2153 return errs
2148 return errs
2154
2149
2155 def revert(ui, repo, *pats, **opts):
2150 def revert(ui, repo, *pats, **opts):
2156 """revert files or dirs to their states as of some revision
2151 """revert files or dirs to their states as of some revision
2157
2152
2158 With no revision specified, revert the named files or directories
2153 With no revision specified, revert the named files or directories
2159 to the contents they had in the parent of the working directory.
2154 to the contents they had in the parent of the working directory.
2160 This restores the contents of the affected files to an unmodified
2155 This restores the contents of the affected files to an unmodified
2161 state and unschedules adds, removes, copies, and renames. If the
2156 state and unschedules adds, removes, copies, and renames. If the
2162 working directory has two parents, you must explicitly specify the
2157 working directory has two parents, you must explicitly specify the
2163 revision to revert to.
2158 revision to revert to.
2164
2159
2165 Modified files are saved with a .orig suffix before reverting.
2160 Modified files are saved with a .orig suffix before reverting.
2166 To disable these backups, use --no-backup.
2161 To disable these backups, use --no-backup.
2167
2162
2168 Using the -r option, revert the given files or directories to their
2163 Using the -r option, revert the given files or directories to their
2169 contents as of a specific revision. This can be helpful to "roll
2164 contents as of a specific revision. This can be helpful to "roll
2170 back" some or all of a change that should not have been committed.
2165 back" some or all of a change that should not have been committed.
2171
2166
2172 Revert modifies the working directory. It does not commit any
2167 Revert modifies the working directory. It does not commit any
2173 changes, or change the parent of the working directory. If you
2168 changes, or change the parent of the working directory. If you
2174 revert to a revision other than the parent of the working
2169 revert to a revision other than the parent of the working
2175 directory, the reverted files will thus appear modified
2170 directory, the reverted files will thus appear modified
2176 afterwards.
2171 afterwards.
2177
2172
2178 If a file has been deleted, it is recreated. If the executable
2173 If a file has been deleted, it is recreated. If the executable
2179 mode of a file was changed, it is reset.
2174 mode of a file was changed, it is reset.
2180
2175
2181 If names are given, all files matching the names are reverted.
2176 If names are given, all files matching the names are reverted.
2182
2177
2183 If no arguments are given, no files are reverted.
2178 If no arguments are given, no files are reverted.
2184 """
2179 """
2185
2180
2186 if opts["date"]:
2181 if opts["date"]:
2187 if opts["rev"]:
2182 if opts["rev"]:
2188 raise util.Abort(_("you can't specify a revision and a date"))
2183 raise util.Abort(_("you can't specify a revision and a date"))
2189 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2184 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2190
2185
2191 if not pats and not opts['all']:
2186 if not pats and not opts['all']:
2192 raise util.Abort(_('no files or directories specified; '
2187 raise util.Abort(_('no files or directories specified; '
2193 'use --all to revert the whole repo'))
2188 'use --all to revert the whole repo'))
2194
2189
2195 parent, p2 = repo.dirstate.parents()
2190 parent, p2 = repo.dirstate.parents()
2196 if not opts['rev'] and p2 != nullid:
2191 if not opts['rev'] and p2 != nullid:
2197 raise util.Abort(_('uncommitted merge - please provide a '
2192 raise util.Abort(_('uncommitted merge - please provide a '
2198 'specific revision'))
2193 'specific revision'))
2199 ctx = repo.changectx(opts['rev'])
2194 ctx = repo.changectx(opts['rev'])
2200 node = ctx.node()
2195 node = ctx.node()
2201 mf = ctx.manifest()
2196 mf = ctx.manifest()
2202 if node == parent:
2197 if node == parent:
2203 pmf = mf
2198 pmf = mf
2204 else:
2199 else:
2205 pmf = None
2200 pmf = None
2206
2201
2207 wlock = repo.wlock()
2202 wlock = repo.wlock()
2208
2203
2209 # need all matching names in dirstate and manifest of target rev,
2204 # need all matching names in dirstate and manifest of target rev,
2210 # so have to walk both. do not print errors if files exist in one
2205 # so have to walk both. do not print errors if files exist in one
2211 # but not other.
2206 # but not other.
2212
2207
2213 names = {}
2208 names = {}
2214 target_only = {}
2209 target_only = {}
2215
2210
2216 # walk dirstate.
2211 # walk dirstate.
2217
2212
2218 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2213 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2219 badmatch=mf.has_key):
2214 badmatch=mf.has_key):
2220 names[abs] = (rel, exact)
2215 names[abs] = (rel, exact)
2221 if src == 'b':
2216 if src == 'b':
2222 target_only[abs] = True
2217 target_only[abs] = True
2223
2218
2224 # walk target manifest.
2219 # walk target manifest.
2225
2220
2226 def badmatch(path):
2221 def badmatch(path):
2227 if path in names:
2222 if path in names:
2228 return True
2223 return True
2229 path_ = path + '/'
2224 path_ = path + '/'
2230 for f in names:
2225 for f in names:
2231 if f.startswith(path_):
2226 if f.startswith(path_):
2232 return True
2227 return True
2233 return False
2228 return False
2234
2229
2235 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2230 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2236 badmatch=badmatch):
2231 badmatch=badmatch):
2237 if abs in names or src == 'b':
2232 if abs in names or src == 'b':
2238 continue
2233 continue
2239 names[abs] = (rel, exact)
2234 names[abs] = (rel, exact)
2240 target_only[abs] = True
2235 target_only[abs] = True
2241
2236
2242 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2237 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2243 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2238 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2244
2239
2245 revert = ([], _('reverting %s\n'))
2240 revert = ([], _('reverting %s\n'))
2246 add = ([], _('adding %s\n'))
2241 add = ([], _('adding %s\n'))
2247 remove = ([], _('removing %s\n'))
2242 remove = ([], _('removing %s\n'))
2248 forget = ([], _('forgetting %s\n'))
2243 forget = ([], _('forgetting %s\n'))
2249 undelete = ([], _('undeleting %s\n'))
2244 undelete = ([], _('undeleting %s\n'))
2250 update = {}
2245 update = {}
2251
2246
2252 disptable = (
2247 disptable = (
2253 # dispatch table:
2248 # dispatch table:
2254 # file state
2249 # file state
2255 # action if in target manifest
2250 # action if in target manifest
2256 # action if not in target manifest
2251 # action if not in target manifest
2257 # make backup if in target manifest
2252 # make backup if in target manifest
2258 # make backup if not in target manifest
2253 # make backup if not in target manifest
2259 (modified, revert, remove, True, True),
2254 (modified, revert, remove, True, True),
2260 (added, revert, forget, True, False),
2255 (added, revert, forget, True, False),
2261 (removed, undelete, None, False, False),
2256 (removed, undelete, None, False, False),
2262 (deleted, revert, remove, False, False),
2257 (deleted, revert, remove, False, False),
2263 (unknown, add, None, True, False),
2258 (unknown, add, None, True, False),
2264 (target_only, add, None, False, False),
2259 (target_only, add, None, False, False),
2265 )
2260 )
2266
2261
2267 entries = names.items()
2262 entries = names.items()
2268 entries.sort()
2263 entries.sort()
2269
2264
2270 for abs, (rel, exact) in entries:
2265 for abs, (rel, exact) in entries:
2271 mfentry = mf.get(abs)
2266 mfentry = mf.get(abs)
2272 def handle(xlist, dobackup):
2267 def handle(xlist, dobackup):
2273 xlist[0].append(abs)
2268 xlist[0].append(abs)
2274 update[abs] = 1
2269 update[abs] = 1
2275 if (dobackup and not opts['no_backup'] and
2270 if (dobackup and not opts['no_backup'] and
2276 (os.path.islink(rel) or os.path.exists(rel))):
2271 (os.path.islink(rel) or os.path.exists(rel))):
2277 bakname = "%s.orig" % rel
2272 bakname = "%s.orig" % rel
2278 ui.note(_('saving current version of %s as %s\n') %
2273 ui.note(_('saving current version of %s as %s\n') %
2279 (rel, bakname))
2274 (rel, bakname))
2280 if not opts.get('dry_run'):
2275 if not opts.get('dry_run'):
2281 util.copyfile(rel, bakname)
2276 util.copyfile(rel, bakname)
2282 if ui.verbose or not exact:
2277 if ui.verbose or not exact:
2283 ui.status(xlist[1] % rel)
2278 ui.status(xlist[1] % rel)
2284 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2279 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2285 if abs not in table: continue
2280 if abs not in table: continue
2286 # file has changed in dirstate
2281 # file has changed in dirstate
2287 if mfentry:
2282 if mfentry:
2288 handle(hitlist, backuphit)
2283 handle(hitlist, backuphit)
2289 elif misslist is not None:
2284 elif misslist is not None:
2290 handle(misslist, backupmiss)
2285 handle(misslist, backupmiss)
2291 else:
2286 else:
2292 if exact: ui.warn(_('file not managed: %s\n') % rel)
2287 if exact: ui.warn(_('file not managed: %s\n') % rel)
2293 break
2288 break
2294 else:
2289 else:
2295 # file has not changed in dirstate
2290 # file has not changed in dirstate
2296 if node == parent:
2291 if node == parent:
2297 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2292 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2298 continue
2293 continue
2299 if pmf is None:
2294 if pmf is None:
2300 # only need parent manifest in this unlikely case,
2295 # only need parent manifest in this unlikely case,
2301 # so do not read by default
2296 # so do not read by default
2302 pmf = repo.changectx(parent).manifest()
2297 pmf = repo.changectx(parent).manifest()
2303 if abs in pmf:
2298 if abs in pmf:
2304 if mfentry:
2299 if mfentry:
2305 # if version of file is same in parent and target
2300 # if version of file is same in parent and target
2306 # manifests, do nothing
2301 # manifests, do nothing
2307 if pmf[abs] != mfentry:
2302 if pmf[abs] != mfentry:
2308 handle(revert, False)
2303 handle(revert, False)
2309 else:
2304 else:
2310 handle(remove, False)
2305 handle(remove, False)
2311
2306
2312 if not opts.get('dry_run'):
2307 if not opts.get('dry_run'):
2313 repo.dirstate.forget(forget[0])
2308 repo.dirstate.forget(forget[0])
2314 r = hg.revert(repo, node, update.has_key, wlock)
2309 r = hg.revert(repo, node, update.has_key, wlock)
2315 repo.dirstate.update(add[0], 'a')
2310 repo.dirstate.update(add[0], 'a')
2316 repo.dirstate.update(undelete[0], 'n')
2311 repo.dirstate.update(undelete[0], 'n')
2317 repo.dirstate.update(remove[0], 'r')
2312 repo.dirstate.update(remove[0], 'r')
2318 return r
2313 return r
2319
2314
2320 def rollback(ui, repo):
2315 def rollback(ui, repo):
2321 """roll back the last transaction in this repository
2316 """roll back the last transaction in this repository
2322
2317
2323 Roll back the last transaction in this repository, restoring the
2318 Roll back the last transaction in this repository, restoring the
2324 project to its state prior to the transaction.
2319 project to its state prior to the transaction.
2325
2320
2326 Transactions are used to encapsulate the effects of all commands
2321 Transactions are used to encapsulate the effects of all commands
2327 that create new changesets or propagate existing changesets into a
2322 that create new changesets or propagate existing changesets into a
2328 repository. For example, the following commands are transactional,
2323 repository. For example, the following commands are transactional,
2329 and their effects can be rolled back:
2324 and their effects can be rolled back:
2330
2325
2331 commit
2326 commit
2332 import
2327 import
2333 pull
2328 pull
2334 push (with this repository as destination)
2329 push (with this repository as destination)
2335 unbundle
2330 unbundle
2336
2331
2337 This command should be used with care. There is only one level of
2332 This command should be used with care. There is only one level of
2338 rollback, and there is no way to undo a rollback.
2333 rollback, and there is no way to undo a rollback.
2339
2334
2340 This command is not intended for use on public repositories. Once
2335 This command is not intended for use on public repositories. Once
2341 changes are visible for pull by other users, rolling a transaction
2336 changes are visible for pull by other users, rolling a transaction
2342 back locally is ineffective (someone else may already have pulled
2337 back locally is ineffective (someone else may already have pulled
2343 the changes). Furthermore, a race is possible with readers of the
2338 the changes). Furthermore, a race is possible with readers of the
2344 repository; for example an in-progress pull from the repository
2339 repository; for example an in-progress pull from the repository
2345 may fail if a rollback is performed.
2340 may fail if a rollback is performed.
2346 """
2341 """
2347 repo.rollback()
2342 repo.rollback()
2348
2343
2349 def root(ui, repo):
2344 def root(ui, repo):
2350 """print the root (top) of the current working dir
2345 """print the root (top) of the current working dir
2351
2346
2352 Print the root directory of the current repository.
2347 Print the root directory of the current repository.
2353 """
2348 """
2354 ui.write(repo.root + "\n")
2349 ui.write(repo.root + "\n")
2355
2350
2356 def serve(ui, repo, **opts):
2351 def serve(ui, repo, **opts):
2357 """export the repository via HTTP
2352 """export the repository via HTTP
2358
2353
2359 Start a local HTTP repository browser and pull server.
2354 Start a local HTTP repository browser and pull server.
2360
2355
2361 By default, the server logs accesses to stdout and errors to
2356 By default, the server logs accesses to stdout and errors to
2362 stderr. Use the "-A" and "-E" options to log to files.
2357 stderr. Use the "-A" and "-E" options to log to files.
2363 """
2358 """
2364
2359
2365 if opts["stdio"]:
2360 if opts["stdio"]:
2366 if repo is None:
2361 if repo is None:
2367 raise hg.RepoError(_("There is no Mercurial repository here"
2362 raise hg.RepoError(_("There is no Mercurial repository here"
2368 " (.hg not found)"))
2363 " (.hg not found)"))
2369 s = sshserver.sshserver(ui, repo)
2364 s = sshserver.sshserver(ui, repo)
2370 s.serve_forever()
2365 s.serve_forever()
2371
2366
2372 parentui = ui.parentui or ui
2367 parentui = ui.parentui or ui
2373 optlist = ("name templates style address port ipv6"
2368 optlist = ("name templates style address port ipv6"
2374 " accesslog errorlog webdir_conf")
2369 " accesslog errorlog webdir_conf")
2375 for o in optlist.split():
2370 for o in optlist.split():
2376 if opts[o]:
2371 if opts[o]:
2377 parentui.setconfig("web", o, str(opts[o]))
2372 parentui.setconfig("web", o, str(opts[o]))
2378
2373
2379 if repo is None and not ui.config("web", "webdir_conf"):
2374 if repo is None and not ui.config("web", "webdir_conf"):
2380 raise hg.RepoError(_("There is no Mercurial repository here"
2375 raise hg.RepoError(_("There is no Mercurial repository here"
2381 " (.hg not found)"))
2376 " (.hg not found)"))
2382
2377
2383 class service:
2378 class service:
2384 def init(self):
2379 def init(self):
2385 try:
2380 try:
2386 self.httpd = hgweb.server.create_server(parentui, repo)
2381 self.httpd = hgweb.server.create_server(parentui, repo)
2387 except socket.error, inst:
2382 except socket.error, inst:
2388 raise util.Abort(_('cannot start server: ') + inst.args[1])
2383 raise util.Abort(_('cannot start server: ') + inst.args[1])
2389
2384
2390 if not ui.verbose: return
2385 if not ui.verbose: return
2391
2386
2392 if httpd.port != 80:
2387 if httpd.port != 80:
2393 ui.status(_('listening at http://%s:%d/\n') %
2388 ui.status(_('listening at http://%s:%d/\n') %
2394 (httpd.addr, httpd.port))
2389 (httpd.addr, httpd.port))
2395 else:
2390 else:
2396 ui.status(_('listening at http://%s/\n') % httpd.addr)
2391 ui.status(_('listening at http://%s/\n') % httpd.addr)
2397
2392
2398 def run(self):
2393 def run(self):
2399 self.httpd.serve_forever()
2394 self.httpd.serve_forever()
2400
2395
2401 service = service()
2396 service = service()
2402
2397
2403 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2398 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2404
2399
2405 def status(ui, repo, *pats, **opts):
2400 def status(ui, repo, *pats, **opts):
2406 """show changed files in the working directory
2401 """show changed files in the working directory
2407
2402
2408 Show status of files in the repository. If names are given, only
2403 Show status of files in the repository. If names are given, only
2409 files that match are shown. Files that are clean or ignored, are
2404 files that match are shown. Files that are clean or ignored, are
2410 not listed unless -c (clean), -i (ignored) or -A is given.
2405 not listed unless -c (clean), -i (ignored) or -A is given.
2411
2406
2412 NOTE: status may appear to disagree with diff if permissions have
2407 NOTE: status may appear to disagree with diff if permissions have
2413 changed or a merge has occurred. The standard diff format does not
2408 changed or a merge has occurred. The standard diff format does not
2414 report permission changes and diff only reports changes relative
2409 report permission changes and diff only reports changes relative
2415 to one merge parent.
2410 to one merge parent.
2416
2411
2417 If one revision is given, it is used as the base revision.
2412 If one revision is given, it is used as the base revision.
2418 If two revisions are given, the difference between them is shown.
2413 If two revisions are given, the difference between them is shown.
2419
2414
2420 The codes used to show the status of files are:
2415 The codes used to show the status of files are:
2421 M = modified
2416 M = modified
2422 A = added
2417 A = added
2423 R = removed
2418 R = removed
2424 C = clean
2419 C = clean
2425 ! = deleted, but still tracked
2420 ! = deleted, but still tracked
2426 ? = not tracked
2421 ? = not tracked
2427 I = ignored (not shown by default)
2422 I = ignored (not shown by default)
2428 = the previous added file was copied from here
2423 = the previous added file was copied from here
2429 """
2424 """
2430
2425
2431 all = opts['all']
2426 all = opts['all']
2432 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2427 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2433
2428
2434 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2429 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2435 cwd = (pats and repo.getcwd()) or ''
2430 cwd = (pats and repo.getcwd()) or ''
2436 modified, added, removed, deleted, unknown, ignored, clean = [
2431 modified, added, removed, deleted, unknown, ignored, clean = [
2437 n for n in repo.status(node1=node1, node2=node2, files=files,
2432 n for n in repo.status(node1=node1, node2=node2, files=files,
2438 match=matchfn,
2433 match=matchfn,
2439 list_ignored=all or opts['ignored'],
2434 list_ignored=all or opts['ignored'],
2440 list_clean=all or opts['clean'])]
2435 list_clean=all or opts['clean'])]
2441
2436
2442 changetypes = (('modified', 'M', modified),
2437 changetypes = (('modified', 'M', modified),
2443 ('added', 'A', added),
2438 ('added', 'A', added),
2444 ('removed', 'R', removed),
2439 ('removed', 'R', removed),
2445 ('deleted', '!', deleted),
2440 ('deleted', '!', deleted),
2446 ('unknown', '?', unknown),
2441 ('unknown', '?', unknown),
2447 ('ignored', 'I', ignored))
2442 ('ignored', 'I', ignored))
2448
2443
2449 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2444 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2450
2445
2451 end = opts['print0'] and '\0' or '\n'
2446 end = opts['print0'] and '\0' or '\n'
2452
2447
2453 for opt, char, changes in ([ct for ct in explicit_changetypes
2448 for opt, char, changes in ([ct for ct in explicit_changetypes
2454 if all or opts[ct[0]]]
2449 if all or opts[ct[0]]]
2455 or changetypes):
2450 or changetypes):
2456 if opts['no_status']:
2451 if opts['no_status']:
2457 format = "%%s%s" % end
2452 format = "%%s%s" % end
2458 else:
2453 else:
2459 format = "%s %%s%s" % (char, end)
2454 format = "%s %%s%s" % (char, end)
2460
2455
2461 for f in changes:
2456 for f in changes:
2462 ui.write(format % util.pathto(repo.root, cwd, f))
2457 ui.write(format % util.pathto(repo.root, cwd, f))
2463 if ((all or opts.get('copies')) and not opts.get('no_status')):
2458 if ((all or opts.get('copies')) and not opts.get('no_status')):
2464 copied = repo.dirstate.copied(f)
2459 copied = repo.dirstate.copied(f)
2465 if copied:
2460 if copied:
2466 ui.write(' %s%s' % (util.pathto(repo.root, cwd, copied),
2461 ui.write(' %s%s' % (util.pathto(repo.root, cwd, copied),
2467 end))
2462 end))
2468
2463
2469 def tag(ui, repo, name, rev_=None, **opts):
2464 def tag(ui, repo, name, rev_=None, **opts):
2470 """add a tag for the current or given revision
2465 """add a tag for the current or given revision
2471
2466
2472 Name a particular revision using <name>.
2467 Name a particular revision using <name>.
2473
2468
2474 Tags are used to name particular revisions of the repository and are
2469 Tags are used to name particular revisions of the repository and are
2475 very useful to compare different revision, to go back to significant
2470 very useful to compare different revision, to go back to significant
2476 earlier versions or to mark branch points as releases, etc.
2471 earlier versions or to mark branch points as releases, etc.
2477
2472
2478 If no revision is given, the parent of the working directory is used,
2473 If no revision is given, the parent of the working directory is used,
2479 or tip if no revision is checked out.
2474 or tip if no revision is checked out.
2480
2475
2481 To facilitate version control, distribution, and merging of tags,
2476 To facilitate version control, distribution, and merging of tags,
2482 they are stored as a file named ".hgtags" which is managed
2477 they are stored as a file named ".hgtags" which is managed
2483 similarly to other project files and can be hand-edited if
2478 similarly to other project files and can be hand-edited if
2484 necessary. The file '.hg/localtags' is used for local tags (not
2479 necessary. The file '.hg/localtags' is used for local tags (not
2485 shared among repositories).
2480 shared among repositories).
2486 """
2481 """
2487 if name in ['tip', '.', 'null']:
2482 if name in ['tip', '.', 'null']:
2488 raise util.Abort(_("the name '%s' is reserved") % name)
2483 raise util.Abort(_("the name '%s' is reserved") % name)
2489 if rev_ is not None:
2484 if rev_ is not None:
2490 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2485 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2491 "please use 'hg tag [-r REV] NAME' instead\n"))
2486 "please use 'hg tag [-r REV] NAME' instead\n"))
2492 if opts['rev']:
2487 if opts['rev']:
2493 raise util.Abort(_("use only one form to specify the revision"))
2488 raise util.Abort(_("use only one form to specify the revision"))
2494 if opts['rev'] and opts['remove']:
2489 if opts['rev'] and opts['remove']:
2495 raise util.Abort(_("--rev and --remove are incompatible"))
2490 raise util.Abort(_("--rev and --remove are incompatible"))
2496 if opts['rev']:
2491 if opts['rev']:
2497 rev_ = opts['rev']
2492 rev_ = opts['rev']
2498 message = opts['message']
2493 message = opts['message']
2499 if opts['remove']:
2494 if opts['remove']:
2500 rev_ = nullid
2495 rev_ = nullid
2501 if not message:
2496 if not message:
2502 message = _('Removed tag %s') % name
2497 message = _('Removed tag %s') % name
2503 elif name in repo.tags() and not opts['force']:
2498 elif name in repo.tags() and not opts['force']:
2504 raise util.Abort(_('a tag named %s already exists (use -f to force)')
2499 raise util.Abort(_('a tag named %s already exists (use -f to force)')
2505 % name)
2500 % name)
2506 if not rev_ and repo.dirstate.parents()[1] != nullid:
2501 if not rev_ and repo.dirstate.parents()[1] != nullid:
2507 raise util.Abort(_('uncommitted merge - please provide a '
2502 raise util.Abort(_('uncommitted merge - please provide a '
2508 'specific revision'))
2503 'specific revision'))
2509 r = repo.changectx(rev_).node()
2504 r = repo.changectx(rev_).node()
2510
2505
2511 if not message:
2506 if not message:
2512 message = _('Added tag %s for changeset %s') % (name, short(r))
2507 message = _('Added tag %s for changeset %s') % (name, short(r))
2513
2508
2514 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2509 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2515
2510
2516 def tags(ui, repo):
2511 def tags(ui, repo):
2517 """list repository tags
2512 """list repository tags
2518
2513
2519 List the repository tags.
2514 List the repository tags.
2520
2515
2521 This lists both regular and local tags.
2516 This lists both regular and local tags.
2522 """
2517 """
2523
2518
2524 l = repo.tagslist()
2519 l = repo.tagslist()
2525 l.reverse()
2520 l.reverse()
2526 hexfunc = ui.debugflag and hex or short
2521 hexfunc = ui.debugflag and hex or short
2527 for t, n in l:
2522 for t, n in l:
2528 try:
2523 try:
2529 hn = hexfunc(n)
2524 hn = hexfunc(n)
2530 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2525 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2531 except revlog.LookupError:
2526 except revlog.LookupError:
2532 r = " ?:%s" % hn
2527 r = " ?:%s" % hn
2533 if ui.quiet:
2528 if ui.quiet:
2534 ui.write("%s\n" % t)
2529 ui.write("%s\n" % t)
2535 else:
2530 else:
2536 spaces = " " * (30 - util.locallen(t))
2531 spaces = " " * (30 - util.locallen(t))
2537 ui.write("%s%s %s\n" % (t, spaces, r))
2532 ui.write("%s%s %s\n" % (t, spaces, r))
2538
2533
2539 def tip(ui, repo, **opts):
2534 def tip(ui, repo, **opts):
2540 """show the tip revision
2535 """show the tip revision
2541
2536
2542 Show the tip revision.
2537 Show the tip revision.
2543 """
2538 """
2544 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2539 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2545
2540
2546 def unbundle(ui, repo, fname, **opts):
2541 def unbundle(ui, repo, fname, **opts):
2547 """apply a changegroup file
2542 """apply a changegroup file
2548
2543
2549 Apply a compressed changegroup file generated by the bundle
2544 Apply a compressed changegroup file generated by the bundle
2550 command.
2545 command.
2551 """
2546 """
2552 if os.path.exists(fname):
2547 if os.path.exists(fname):
2553 f = open(fname, "rb")
2548 f = open(fname, "rb")
2554 else:
2549 else:
2555 f = urllib.urlopen(fname)
2550 f = urllib.urlopen(fname)
2556 gen = changegroup.readbundle(f, fname)
2551 gen = changegroup.readbundle(f, fname)
2557 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2552 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2558 return postincoming(ui, repo, modheads, opts['update'])
2553 return postincoming(ui, repo, modheads, opts['update'])
2559
2554
2560 def update(ui, repo, node=None, clean=False, date=None):
2555 def update(ui, repo, node=None, clean=False, date=None):
2561 """update working directory
2556 """update working directory
2562
2557
2563 Update the working directory to the specified revision, or the
2558 Update the working directory to the specified revision, or the
2564 tip of the current branch if none is specified.
2559 tip of the current branch if none is specified.
2565
2560
2566 If there are no outstanding changes in the working directory and
2561 If there are no outstanding changes in the working directory and
2567 there is a linear relationship between the current version and the
2562 there is a linear relationship between the current version and the
2568 requested version, the result is the requested version.
2563 requested version, the result is the requested version.
2569
2564
2570 To merge the working directory with another revision, use the
2565 To merge the working directory with another revision, use the
2571 merge command.
2566 merge command.
2572
2567
2573 By default, update will refuse to run if doing so would require
2568 By default, update will refuse to run if doing so would require
2574 discarding local changes.
2569 discarding local changes.
2575 """
2570 """
2576 if date:
2571 if date:
2577 if node:
2572 if node:
2578 raise util.Abort(_("you can't specify a revision and a date"))
2573 raise util.Abort(_("you can't specify a revision and a date"))
2579 node = cmdutil.finddate(ui, repo, date)
2574 node = cmdutil.finddate(ui, repo, date)
2580
2575
2581 if clean:
2576 if clean:
2582 return hg.clean(repo, node)
2577 return hg.clean(repo, node)
2583 else:
2578 else:
2584 return hg.update(repo, node)
2579 return hg.update(repo, node)
2585
2580
2586 def verify(ui, repo):
2581 def verify(ui, repo):
2587 """verify the integrity of the repository
2582 """verify the integrity of the repository
2588
2583
2589 Verify the integrity of the current repository.
2584 Verify the integrity of the current repository.
2590
2585
2591 This will perform an extensive check of the repository's
2586 This will perform an extensive check of the repository's
2592 integrity, validating the hashes and checksums of each entry in
2587 integrity, validating the hashes and checksums of each entry in
2593 the changelog, manifest, and tracked files, as well as the
2588 the changelog, manifest, and tracked files, as well as the
2594 integrity of their crosslinks and indices.
2589 integrity of their crosslinks and indices.
2595 """
2590 """
2596 return hg.verify(repo)
2591 return hg.verify(repo)
2597
2592
2598 def version_(ui):
2593 def version_(ui):
2599 """output version and copyright information"""
2594 """output version and copyright information"""
2600 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2595 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2601 % version.get_version())
2596 % version.get_version())
2602 ui.status(_(
2597 ui.status(_(
2603 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
2598 "\nCopyright (C) 2005, 2006 Matt Mackall <mpm@selenic.com>\n"
2604 "This is free software; see the source for copying conditions. "
2599 "This is free software; see the source for copying conditions. "
2605 "There is NO\nwarranty; "
2600 "There is NO\nwarranty; "
2606 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2601 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2607 ))
2602 ))
2608
2603
2609 # Command options and aliases are listed here, alphabetically
2604 # Command options and aliases are listed here, alphabetically
2610
2605
2611 globalopts = [
2606 globalopts = [
2612 ('R', 'repository', '',
2607 ('R', 'repository', '',
2613 _('repository root directory or symbolic path name')),
2608 _('repository root directory or symbolic path name')),
2614 ('', 'cwd', '', _('change working directory')),
2609 ('', 'cwd', '', _('change working directory')),
2615 ('y', 'noninteractive', None,
2610 ('y', 'noninteractive', None,
2616 _('do not prompt, assume \'yes\' for any required answers')),
2611 _('do not prompt, assume \'yes\' for any required answers')),
2617 ('q', 'quiet', None, _('suppress output')),
2612 ('q', 'quiet', None, _('suppress output')),
2618 ('v', 'verbose', None, _('enable additional output')),
2613 ('v', 'verbose', None, _('enable additional output')),
2619 ('', 'config', [], _('set/override config option')),
2614 ('', 'config', [], _('set/override config option')),
2620 ('', 'debug', None, _('enable debugging output')),
2615 ('', 'debug', None, _('enable debugging output')),
2621 ('', 'debugger', None, _('start debugger')),
2616 ('', 'debugger', None, _('start debugger')),
2622 ('', 'encoding', util._encoding, _('set the charset encoding')),
2617 ('', 'encoding', util._encoding, _('set the charset encoding')),
2623 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2618 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2624 ('', 'lsprof', None, _('print improved command execution profile')),
2619 ('', 'lsprof', None, _('print improved command execution profile')),
2625 ('', 'traceback', None, _('print traceback on exception')),
2620 ('', 'traceback', None, _('print traceback on exception')),
2626 ('', 'time', None, _('time how long the command takes')),
2621 ('', 'time', None, _('time how long the command takes')),
2627 ('', 'profile', None, _('print command execution profile')),
2622 ('', 'profile', None, _('print command execution profile')),
2628 ('', 'version', None, _('output version information and exit')),
2623 ('', 'version', None, _('output version information and exit')),
2629 ('h', 'help', None, _('display help and exit')),
2624 ('h', 'help', None, _('display help and exit')),
2630 ]
2625 ]
2631
2626
2632 dryrunopts = [('n', 'dry-run', None,
2627 dryrunopts = [('n', 'dry-run', None,
2633 _('do not perform actions, just print output'))]
2628 _('do not perform actions, just print output'))]
2634
2629
2635 remoteopts = [
2630 remoteopts = [
2636 ('e', 'ssh', '', _('specify ssh command to use')),
2631 ('e', 'ssh', '', _('specify ssh command to use')),
2637 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2632 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2638 ]
2633 ]
2639
2634
2640 walkopts = [
2635 walkopts = [
2641 ('I', 'include', [], _('include names matching the given patterns')),
2636 ('I', 'include', [], _('include names matching the given patterns')),
2642 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2637 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2643 ]
2638 ]
2644
2639
2645 commitopts = [
2640 commitopts = [
2646 ('m', 'message', '', _('use <text> as commit message')),
2641 ('m', 'message', '', _('use <text> as commit message')),
2647 ('l', 'logfile', '', _('read commit message from <file>')),
2642 ('l', 'logfile', '', _('read commit message from <file>')),
2648 ]
2643 ]
2649
2644
2650 table = {
2645 table = {
2651 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2646 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2652 "addremove":
2647 "addremove":
2653 (addremove,
2648 (addremove,
2654 [('s', 'similarity', '',
2649 [('s', 'similarity', '',
2655 _('guess renamed files by similarity (0<=s<=100)')),
2650 _('guess renamed files by similarity (0<=s<=100)')),
2656 ] + walkopts + dryrunopts,
2651 ] + walkopts + dryrunopts,
2657 _('hg addremove [OPTION]... [FILE]...')),
2652 _('hg addremove [OPTION]... [FILE]...')),
2658 "^annotate":
2653 "^annotate":
2659 (annotate,
2654 (annotate,
2660 [('r', 'rev', '', _('annotate the specified revision')),
2655 [('r', 'rev', '', _('annotate the specified revision')),
2661 ('f', 'follow', None, _('follow file copies and renames')),
2656 ('f', 'follow', None, _('follow file copies and renames')),
2662 ('a', 'text', None, _('treat all files as text')),
2657 ('a', 'text', None, _('treat all files as text')),
2663 ('u', 'user', None, _('list the author')),
2658 ('u', 'user', None, _('list the author')),
2664 ('d', 'date', None, _('list the date')),
2659 ('d', 'date', None, _('list the date')),
2665 ('n', 'number', None, _('list the revision number (default)')),
2660 ('n', 'number', None, _('list the revision number (default)')),
2666 ('c', 'changeset', None, _('list the changeset')),
2661 ('c', 'changeset', None, _('list the changeset')),
2667 ] + walkopts,
2662 ] + walkopts,
2668 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2663 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2669 "archive":
2664 "archive":
2670 (archive,
2665 (archive,
2671 [('', 'no-decode', None, _('do not pass files through decoders')),
2666 [('', 'no-decode', None, _('do not pass files through decoders')),
2672 ('p', 'prefix', '', _('directory prefix for files in archive')),
2667 ('p', 'prefix', '', _('directory prefix for files in archive')),
2673 ('r', 'rev', '', _('revision to distribute')),
2668 ('r', 'rev', '', _('revision to distribute')),
2674 ('t', 'type', '', _('type of distribution to create')),
2669 ('t', 'type', '', _('type of distribution to create')),
2675 ] + walkopts,
2670 ] + walkopts,
2676 _('hg archive [OPTION]... DEST')),
2671 _('hg archive [OPTION]... DEST')),
2677 "backout":
2672 "backout":
2678 (backout,
2673 (backout,
2679 [('', 'merge', None,
2674 [('', 'merge', None,
2680 _('merge with old dirstate parent after backout')),
2675 _('merge with old dirstate parent after backout')),
2681 ('d', 'date', '', _('record datecode as commit date')),
2676 ('d', 'date', '', _('record datecode as commit date')),
2682 ('', 'parent', '', _('parent to choose when backing out merge')),
2677 ('', 'parent', '', _('parent to choose when backing out merge')),
2683 ('u', 'user', '', _('record user as committer')),
2678 ('u', 'user', '', _('record user as committer')),
2684 ] + walkopts + commitopts,
2679 ] + walkopts + commitopts,
2685 _('hg backout [OPTION]... REV')),
2680 _('hg backout [OPTION]... REV')),
2686 "branch": (branch,
2681 "branch": (branch,
2687 [('f', 'force', None,
2682 [('f', 'force', None,
2688 _('set branch name even if it shadows an existing branch'))],
2683 _('set branch name even if it shadows an existing branch'))],
2689 _('hg branch [NAME]')),
2684 _('hg branch [NAME]')),
2690 "branches": (branches, [], _('hg branches')),
2685 "branches": (branches, [], _('hg branches')),
2691 "bundle":
2686 "bundle":
2692 (bundle,
2687 (bundle,
2693 [('f', 'force', None,
2688 [('f', 'force', None,
2694 _('run even when remote repository is unrelated')),
2689 _('run even when remote repository is unrelated')),
2695 ('r', 'rev', [],
2690 ('r', 'rev', [],
2696 _('a changeset you would like to bundle')),
2691 _('a changeset you would like to bundle')),
2697 ('', 'base', [],
2692 ('', 'base', [],
2698 _('a base changeset to specify instead of a destination')),
2693 _('a base changeset to specify instead of a destination')),
2699 ] + remoteopts,
2694 ] + remoteopts,
2700 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2695 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2701 "cat":
2696 "cat":
2702 (cat,
2697 (cat,
2703 [('o', 'output', '', _('print output to file with formatted name')),
2698 [('o', 'output', '', _('print output to file with formatted name')),
2704 ('r', 'rev', '', _('print the given revision')),
2699 ('r', 'rev', '', _('print the given revision')),
2705 ] + walkopts,
2700 ] + walkopts,
2706 _('hg cat [OPTION]... FILE...')),
2701 _('hg cat [OPTION]... FILE...')),
2707 "^clone":
2702 "^clone":
2708 (clone,
2703 (clone,
2709 [('U', 'noupdate', None, _('do not update the new working directory')),
2704 [('U', 'noupdate', None, _('do not update the new working directory')),
2710 ('r', 'rev', [],
2705 ('r', 'rev', [],
2711 _('a changeset you would like to have after cloning')),
2706 _('a changeset you would like to have after cloning')),
2712 ('', 'pull', None, _('use pull protocol to copy metadata')),
2707 ('', 'pull', None, _('use pull protocol to copy metadata')),
2713 ('', 'uncompressed', None,
2708 ('', 'uncompressed', None,
2714 _('use uncompressed transfer (fast over LAN)')),
2709 _('use uncompressed transfer (fast over LAN)')),
2715 ] + remoteopts,
2710 ] + remoteopts,
2716 _('hg clone [OPTION]... SOURCE [DEST]')),
2711 _('hg clone [OPTION]... SOURCE [DEST]')),
2717 "^commit|ci":
2712 "^commit|ci":
2718 (commit,
2713 (commit,
2719 [('A', 'addremove', None,
2714 [('A', 'addremove', None,
2720 _('mark new/missing files as added/removed before committing')),
2715 _('mark new/missing files as added/removed before committing')),
2721 ('d', 'date', '', _('record datecode as commit date')),
2716 ('d', 'date', '', _('record datecode as commit date')),
2722 ('u', 'user', '', _('record user as commiter')),
2717 ('u', 'user', '', _('record user as commiter')),
2723 ] + walkopts + commitopts,
2718 ] + walkopts + commitopts,
2724 _('hg commit [OPTION]... [FILE]...')),
2719 _('hg commit [OPTION]... [FILE]...')),
2725 "copy|cp":
2720 "copy|cp":
2726 (copy,
2721 (copy,
2727 [('A', 'after', None, _('record a copy that has already occurred')),
2722 [('A', 'after', None, _('record a copy that has already occurred')),
2728 ('f', 'force', None,
2723 ('f', 'force', None,
2729 _('forcibly copy over an existing managed file')),
2724 _('forcibly copy over an existing managed file')),
2730 ] + walkopts + dryrunopts,
2725 ] + walkopts + dryrunopts,
2731 _('hg copy [OPTION]... [SOURCE]... DEST')),
2726 _('hg copy [OPTION]... [SOURCE]... DEST')),
2732 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2727 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2733 "debugcomplete":
2728 "debugcomplete":
2734 (debugcomplete,
2729 (debugcomplete,
2735 [('o', 'options', None, _('show the command options'))],
2730 [('o', 'options', None, _('show the command options'))],
2736 _('debugcomplete [-o] CMD')),
2731 _('debugcomplete [-o] CMD')),
2737 "debuginstall": (debuginstall, [], _('debuginstall')),
2732 "debuginstall": (debuginstall, [], _('debuginstall')),
2738 "debugrebuildstate":
2733 "debugrebuildstate":
2739 (debugrebuildstate,
2734 (debugrebuildstate,
2740 [('r', 'rev', '', _('revision to rebuild to'))],
2735 [('r', 'rev', '', _('revision to rebuild to'))],
2741 _('debugrebuildstate [-r REV] [REV]')),
2736 _('debugrebuildstate [-r REV] [REV]')),
2742 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2737 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2743 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2738 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2744 "debugstate": (debugstate, [], _('debugstate')),
2739 "debugstate": (debugstate, [], _('debugstate')),
2745 "debugdate":
2740 "debugdate":
2746 (debugdate,
2741 (debugdate,
2747 [('e', 'extended', None, _('try extended date formats'))],
2742 [('e', 'extended', None, _('try extended date formats'))],
2748 _('debugdate [-e] DATE [RANGE]')),
2743 _('debugdate [-e] DATE [RANGE]')),
2749 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2744 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2750 "debugindex": (debugindex, [], _('debugindex FILE')),
2745 "debugindex": (debugindex, [], _('debugindex FILE')),
2751 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2746 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2752 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2747 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2753 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2748 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2754 "^diff":
2749 "^diff":
2755 (diff,
2750 (diff,
2756 [('r', 'rev', [], _('revision')),
2751 [('r', 'rev', [], _('revision')),
2757 ('a', 'text', None, _('treat all files as text')),
2752 ('a', 'text', None, _('treat all files as text')),
2758 ('p', 'show-function', None,
2753 ('p', 'show-function', None,
2759 _('show which function each change is in')),
2754 _('show which function each change is in')),
2760 ('g', 'git', None, _('use git extended diff format')),
2755 ('g', 'git', None, _('use git extended diff format')),
2761 ('', 'nodates', None, _("don't include dates in diff headers")),
2756 ('', 'nodates', None, _("don't include dates in diff headers")),
2762 ('w', 'ignore-all-space', None,
2757 ('w', 'ignore-all-space', None,
2763 _('ignore white space when comparing lines')),
2758 _('ignore white space when comparing lines')),
2764 ('b', 'ignore-space-change', None,
2759 ('b', 'ignore-space-change', None,
2765 _('ignore changes in the amount of white space')),
2760 _('ignore changes in the amount of white space')),
2766 ('B', 'ignore-blank-lines', None,
2761 ('B', 'ignore-blank-lines', None,
2767 _('ignore changes whose lines are all blank')),
2762 _('ignore changes whose lines are all blank')),
2768 ] + walkopts,
2763 ] + walkopts,
2769 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2764 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2770 "^export":
2765 "^export":
2771 (export,
2766 (export,
2772 [('o', 'output', '', _('print output to file with formatted name')),
2767 [('o', 'output', '', _('print output to file with formatted name')),
2773 ('a', 'text', None, _('treat all files as text')),
2768 ('a', 'text', None, _('treat all files as text')),
2774 ('g', 'git', None, _('use git extended diff format')),
2769 ('g', 'git', None, _('use git extended diff format')),
2775 ('', 'nodates', None, _("don't include dates in diff headers")),
2770 ('', 'nodates', None, _("don't include dates in diff headers")),
2776 ('', 'switch-parent', None, _('diff against the second parent'))],
2771 ('', 'switch-parent', None, _('diff against the second parent'))],
2777 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2772 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2778 "grep":
2773 "grep":
2779 (grep,
2774 (grep,
2780 [('0', 'print0', None, _('end fields with NUL')),
2775 [('0', 'print0', None, _('end fields with NUL')),
2781 ('', 'all', None, _('print all revisions that match')),
2776 ('', 'all', None, _('print all revisions that match')),
2782 ('f', 'follow', None,
2777 ('f', 'follow', None,
2783 _('follow changeset history, or file history across copies and renames')),
2778 _('follow changeset history, or file history across copies and renames')),
2784 ('i', 'ignore-case', None, _('ignore case when matching')),
2779 ('i', 'ignore-case', None, _('ignore case when matching')),
2785 ('l', 'files-with-matches', None,
2780 ('l', 'files-with-matches', None,
2786 _('print only filenames and revs that match')),
2781 _('print only filenames and revs that match')),
2787 ('n', 'line-number', None, _('print matching line numbers')),
2782 ('n', 'line-number', None, _('print matching line numbers')),
2788 ('r', 'rev', [], _('search in given revision range')),
2783 ('r', 'rev', [], _('search in given revision range')),
2789 ('u', 'user', None, _('print user who committed change')),
2784 ('u', 'user', None, _('print user who committed change')),
2790 ] + walkopts,
2785 ] + walkopts,
2791 _('hg grep [OPTION]... PATTERN [FILE]...')),
2786 _('hg grep [OPTION]... PATTERN [FILE]...')),
2792 "heads":
2787 "heads":
2793 (heads,
2788 (heads,
2794 [('', 'style', '', _('display using template map file')),
2789 [('', 'style', '', _('display using template map file')),
2795 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2790 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2796 ('', 'template', '', _('display with template'))],
2791 ('', 'template', '', _('display with template'))],
2797 _('hg heads [-r REV]')),
2792 _('hg heads [-r REV]')),
2798 "help": (help_, [], _('hg help [COMMAND]')),
2793 "help": (help_, [], _('hg help [COMMAND]')),
2799 "identify|id": (identify, [], _('hg identify')),
2794 "identify|id": (identify, [], _('hg identify')),
2800 "import|patch":
2795 "import|patch":
2801 (import_,
2796 (import_,
2802 [('p', 'strip', 1,
2797 [('p', 'strip', 1,
2803 _('directory strip option for patch. This has the same\n'
2798 _('directory strip option for patch. This has the same\n'
2804 'meaning as the corresponding patch option')),
2799 'meaning as the corresponding patch option')),
2805 ('b', 'base', '', _('base path')),
2800 ('b', 'base', '', _('base path')),
2806 ('f', 'force', None,
2801 ('f', 'force', None,
2807 _('skip check for outstanding uncommitted changes')),
2802 _('skip check for outstanding uncommitted changes')),
2808 ('', 'exact', None,
2803 ('', 'exact', None,
2809 _('apply patch to the nodes from which it was generated'))] + commitopts,
2804 _('apply patch to the nodes from which it was generated'))] + commitopts,
2810 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2805 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2811 "incoming|in": (incoming,
2806 "incoming|in": (incoming,
2812 [('M', 'no-merges', None, _('do not show merges')),
2807 [('M', 'no-merges', None, _('do not show merges')),
2813 ('f', 'force', None,
2808 ('f', 'force', None,
2814 _('run even when remote repository is unrelated')),
2809 _('run even when remote repository is unrelated')),
2815 ('', 'style', '', _('display using template map file')),
2810 ('', 'style', '', _('display using template map file')),
2816 ('n', 'newest-first', None, _('show newest record first')),
2811 ('n', 'newest-first', None, _('show newest record first')),
2817 ('', 'bundle', '', _('file to store the bundles into')),
2812 ('', 'bundle', '', _('file to store the bundles into')),
2818 ('p', 'patch', None, _('show patch')),
2813 ('p', 'patch', None, _('show patch')),
2819 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2814 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2820 ('', 'template', '', _('display with template')),
2815 ('', 'template', '', _('display with template')),
2821 ] + remoteopts,
2816 ] + remoteopts,
2822 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2817 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2823 ' [--bundle FILENAME] [SOURCE]')),
2818 ' [--bundle FILENAME] [SOURCE]')),
2824 "^init":
2819 "^init":
2825 (init,
2820 (init,
2826 remoteopts,
2821 remoteopts,
2827 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2822 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2828 "locate":
2823 "locate":
2829 (locate,
2824 (locate,
2830 [('r', 'rev', '', _('search the repository as it stood at rev')),
2825 [('r', 'rev', '', _('search the repository as it stood at rev')),
2831 ('0', 'print0', None,
2826 ('0', 'print0', None,
2832 _('end filenames with NUL, for use with xargs')),
2827 _('end filenames with NUL, for use with xargs')),
2833 ('f', 'fullpath', None,
2828 ('f', 'fullpath', None,
2834 _('print complete paths from the filesystem root')),
2829 _('print complete paths from the filesystem root')),
2835 ] + walkopts,
2830 ] + walkopts,
2836 _('hg locate [OPTION]... [PATTERN]...')),
2831 _('hg locate [OPTION]... [PATTERN]...')),
2837 "^log|history":
2832 "^log|history":
2838 (log,
2833 (log,
2839 [('f', 'follow', None,
2834 [('f', 'follow', None,
2840 _('follow changeset history, or file history across copies and renames')),
2835 _('follow changeset history, or file history across copies and renames')),
2841 ('', 'follow-first', None,
2836 ('', 'follow-first', None,
2842 _('only follow the first parent of merge changesets')),
2837 _('only follow the first parent of merge changesets')),
2843 ('d', 'date', '', _('show revs matching date spec')),
2838 ('d', 'date', '', _('show revs matching date spec')),
2844 ('C', 'copies', None, _('show copied files')),
2839 ('C', 'copies', None, _('show copied files')),
2845 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
2840 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
2846 ('l', 'limit', '', _('limit number of changes displayed')),
2841 ('l', 'limit', '', _('limit number of changes displayed')),
2847 ('r', 'rev', [], _('show the specified revision or range')),
2842 ('r', 'rev', [], _('show the specified revision or range')),
2848 ('', 'removed', None, _('include revs where files were removed')),
2843 ('', 'removed', None, _('include revs where files were removed')),
2849 ('M', 'no-merges', None, _('do not show merges')),
2844 ('M', 'no-merges', None, _('do not show merges')),
2850 ('', 'style', '', _('display using template map file')),
2845 ('', 'style', '', _('display using template map file')),
2851 ('m', 'only-merges', None, _('show only merges')),
2846 ('m', 'only-merges', None, _('show only merges')),
2852 ('p', 'patch', None, _('show patch')),
2847 ('p', 'patch', None, _('show patch')),
2853 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2848 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2854 ('', 'template', '', _('display with template')),
2849 ('', 'template', '', _('display with template')),
2855 ] + walkopts,
2850 ] + walkopts,
2856 _('hg log [OPTION]... [FILE]')),
2851 _('hg log [OPTION]... [FILE]')),
2857 "manifest": (manifest, [], _('hg manifest [REV]')),
2852 "manifest": (manifest, [], _('hg manifest [REV]')),
2858 "^merge":
2853 "^merge":
2859 (merge,
2854 (merge,
2860 [('f', 'force', None, _('force a merge with outstanding changes'))],
2855 [('f', 'force', None, _('force a merge with outstanding changes'))],
2861 _('hg merge [-f] [REV]')),
2856 _('hg merge [-f] [REV]')),
2862 "outgoing|out": (outgoing,
2857 "outgoing|out": (outgoing,
2863 [('M', 'no-merges', None, _('do not show merges')),
2858 [('M', 'no-merges', None, _('do not show merges')),
2864 ('f', 'force', None,
2859 ('f', 'force', None,
2865 _('run even when remote repository is unrelated')),
2860 _('run even when remote repository is unrelated')),
2866 ('p', 'patch', None, _('show patch')),
2861 ('p', 'patch', None, _('show patch')),
2867 ('', 'style', '', _('display using template map file')),
2862 ('', 'style', '', _('display using template map file')),
2868 ('r', 'rev', [], _('a specific revision you would like to push')),
2863 ('r', 'rev', [], _('a specific revision you would like to push')),
2869 ('n', 'newest-first', None, _('show newest record first')),
2864 ('n', 'newest-first', None, _('show newest record first')),
2870 ('', 'template', '', _('display with template')),
2865 ('', 'template', '', _('display with template')),
2871 ] + remoteopts,
2866 ] + remoteopts,
2872 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2867 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2873 "^parents":
2868 "^parents":
2874 (parents,
2869 (parents,
2875 [('r', 'rev', '', _('show parents from the specified rev')),
2870 [('r', 'rev', '', _('show parents from the specified rev')),
2876 ('', 'style', '', _('display using template map file')),
2871 ('', 'style', '', _('display using template map file')),
2877 ('', 'template', '', _('display with template'))],
2872 ('', 'template', '', _('display with template'))],
2878 _('hg parents [-r REV] [FILE]')),
2873 _('hg parents [-r REV] [FILE]')),
2879 "paths": (paths, [], _('hg paths [NAME]')),
2874 "paths": (paths, [], _('hg paths [NAME]')),
2880 "^pull":
2875 "^pull":
2881 (pull,
2876 (pull,
2882 [('u', 'update', None,
2877 [('u', 'update', None,
2883 _('update to new tip if changesets were pulled')),
2878 _('update to new tip if changesets were pulled')),
2884 ('f', 'force', None,
2879 ('f', 'force', None,
2885 _('run even when remote repository is unrelated')),
2880 _('run even when remote repository is unrelated')),
2886 ('r', 'rev', [],
2881 ('r', 'rev', [],
2887 _('a specific revision up to which you would like to pull')),
2882 _('a specific revision up to which you would like to pull')),
2888 ] + remoteopts,
2883 ] + remoteopts,
2889 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
2884 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
2890 "^push":
2885 "^push":
2891 (push,
2886 (push,
2892 [('f', 'force', None, _('force push')),
2887 [('f', 'force', None, _('force push')),
2893 ('r', 'rev', [], _('a specific revision you would like to push')),
2888 ('r', 'rev', [], _('a specific revision you would like to push')),
2894 ] + remoteopts,
2889 ] + remoteopts,
2895 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
2890 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
2896 "debugrawcommit|rawcommit":
2891 "debugrawcommit|rawcommit":
2897 (rawcommit,
2892 (rawcommit,
2898 [('p', 'parent', [], _('parent')),
2893 [('p', 'parent', [], _('parent')),
2899 ('d', 'date', '', _('date code')),
2894 ('d', 'date', '', _('date code')),
2900 ('u', 'user', '', _('user')),
2895 ('u', 'user', '', _('user')),
2901 ('F', 'files', '', _('file list'))
2896 ('F', 'files', '', _('file list'))
2902 ] + commitopts,
2897 ] + commitopts,
2903 _('hg debugrawcommit [OPTION]... [FILE]...')),
2898 _('hg debugrawcommit [OPTION]... [FILE]...')),
2904 "recover": (recover, [], _('hg recover')),
2899 "recover": (recover, [], _('hg recover')),
2905 "^remove|rm":
2900 "^remove|rm":
2906 (remove,
2901 (remove,
2907 [('A', 'after', None, _('record remove that has already occurred')),
2902 [('A', 'after', None, _('record remove that has already occurred')),
2908 ('f', 'force', None, _('remove file even if modified')),
2903 ('f', 'force', None, _('remove file even if modified')),
2909 ] + walkopts,
2904 ] + walkopts,
2910 _('hg remove [OPTION]... FILE...')),
2905 _('hg remove [OPTION]... FILE...')),
2911 "rename|mv":
2906 "rename|mv":
2912 (rename,
2907 (rename,
2913 [('A', 'after', None, _('record a rename that has already occurred')),
2908 [('A', 'after', None, _('record a rename that has already occurred')),
2914 ('f', 'force', None,
2909 ('f', 'force', None,
2915 _('forcibly copy over an existing managed file')),
2910 _('forcibly copy over an existing managed file')),
2916 ] + walkopts + dryrunopts,
2911 ] + walkopts + dryrunopts,
2917 _('hg rename [OPTION]... SOURCE... DEST')),
2912 _('hg rename [OPTION]... SOURCE... DEST')),
2918 "^revert":
2913 "^revert":
2919 (revert,
2914 (revert,
2920 [('a', 'all', None, _('revert all changes when no arguments given')),
2915 [('a', 'all', None, _('revert all changes when no arguments given')),
2921 ('d', 'date', '', _('tipmost revision matching date')),
2916 ('d', 'date', '', _('tipmost revision matching date')),
2922 ('r', 'rev', '', _('revision to revert to')),
2917 ('r', 'rev', '', _('revision to revert to')),
2923 ('', 'no-backup', None, _('do not save backup copies of files')),
2918 ('', 'no-backup', None, _('do not save backup copies of files')),
2924 ] + walkopts + dryrunopts,
2919 ] + walkopts + dryrunopts,
2925 _('hg revert [OPTION]... [-r REV] [NAME]...')),
2920 _('hg revert [OPTION]... [-r REV] [NAME]...')),
2926 "rollback": (rollback, [], _('hg rollback')),
2921 "rollback": (rollback, [], _('hg rollback')),
2927 "root": (root, [], _('hg root')),
2922 "root": (root, [], _('hg root')),
2928 "showconfig|debugconfig":
2923 "showconfig|debugconfig":
2929 (showconfig,
2924 (showconfig,
2930 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2925 [('u', 'untrusted', None, _('show untrusted configuration options'))],
2931 _('showconfig [-u] [NAME]...')),
2926 _('showconfig [-u] [NAME]...')),
2932 "^serve":
2927 "^serve":
2933 (serve,
2928 (serve,
2934 [('A', 'accesslog', '', _('name of access log file to write to')),
2929 [('A', 'accesslog', '', _('name of access log file to write to')),
2935 ('d', 'daemon', None, _('run server in background')),
2930 ('d', 'daemon', None, _('run server in background')),
2936 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2931 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2937 ('E', 'errorlog', '', _('name of error log file to write to')),
2932 ('E', 'errorlog', '', _('name of error log file to write to')),
2938 ('p', 'port', 0, _('port to use (default: 8000)')),
2933 ('p', 'port', 0, _('port to use (default: 8000)')),
2939 ('a', 'address', '', _('address to use')),
2934 ('a', 'address', '', _('address to use')),
2940 ('n', 'name', '',
2935 ('n', 'name', '',
2941 _('name to show in web pages (default: working dir)')),
2936 _('name to show in web pages (default: working dir)')),
2942 ('', 'webdir-conf', '', _('name of the webdir config file'
2937 ('', 'webdir-conf', '', _('name of the webdir config file'
2943 ' (serve more than one repo)')),
2938 ' (serve more than one repo)')),
2944 ('', 'pid-file', '', _('name of file to write process ID to')),
2939 ('', 'pid-file', '', _('name of file to write process ID to')),
2945 ('', 'stdio', None, _('for remote clients')),
2940 ('', 'stdio', None, _('for remote clients')),
2946 ('t', 'templates', '', _('web templates to use')),
2941 ('t', 'templates', '', _('web templates to use')),
2947 ('', 'style', '', _('template style to use')),
2942 ('', 'style', '', _('template style to use')),
2948 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2943 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2949 _('hg serve [OPTION]...')),
2944 _('hg serve [OPTION]...')),
2950 "^status|st":
2945 "^status|st":
2951 (status,
2946 (status,
2952 [('A', 'all', None, _('show status of all files')),
2947 [('A', 'all', None, _('show status of all files')),
2953 ('m', 'modified', None, _('show only modified files')),
2948 ('m', 'modified', None, _('show only modified files')),
2954 ('a', 'added', None, _('show only added files')),
2949 ('a', 'added', None, _('show only added files')),
2955 ('r', 'removed', None, _('show only removed files')),
2950 ('r', 'removed', None, _('show only removed files')),
2956 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2951 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2957 ('c', 'clean', None, _('show only files without changes')),
2952 ('c', 'clean', None, _('show only files without changes')),
2958 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2953 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2959 ('i', 'ignored', None, _('show only ignored files')),
2954 ('i', 'ignored', None, _('show only ignored files')),
2960 ('n', 'no-status', None, _('hide status prefix')),
2955 ('n', 'no-status', None, _('hide status prefix')),
2961 ('C', 'copies', None, _('show source of copied files')),
2956 ('C', 'copies', None, _('show source of copied files')),
2962 ('0', 'print0', None,
2957 ('0', 'print0', None,
2963 _('end filenames with NUL, for use with xargs')),
2958 _('end filenames with NUL, for use with xargs')),
2964 ('', 'rev', [], _('show difference from revision')),
2959 ('', 'rev', [], _('show difference from revision')),
2965 ] + walkopts,
2960 ] + walkopts,
2966 _('hg status [OPTION]... [FILE]...')),
2961 _('hg status [OPTION]... [FILE]...')),
2967 "tag":
2962 "tag":
2968 (tag,
2963 (tag,
2969 [('f', 'force', None, _('replace existing tag')),
2964 [('f', 'force', None, _('replace existing tag')),
2970 ('l', 'local', None, _('make the tag local')),
2965 ('l', 'local', None, _('make the tag local')),
2971 ('m', 'message', '', _('message for tag commit log entry')),
2966 ('m', 'message', '', _('message for tag commit log entry')),
2972 ('d', 'date', '', _('record datecode as commit date')),
2967 ('d', 'date', '', _('record datecode as commit date')),
2973 ('u', 'user', '', _('record user as commiter')),
2968 ('u', 'user', '', _('record user as commiter')),
2974 ('r', 'rev', '', _('revision to tag')),
2969 ('r', 'rev', '', _('revision to tag')),
2975 ('', 'remove', None, _('remove a tag'))],
2970 ('', 'remove', None, _('remove a tag'))],
2976 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2971 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2977 "tags": (tags, [], _('hg tags')),
2972 "tags": (tags, [], _('hg tags')),
2978 "tip":
2973 "tip":
2979 (tip,
2974 (tip,
2980 [('', 'style', '', _('display using template map file')),
2975 [('', 'style', '', _('display using template map file')),
2981 ('p', 'patch', None, _('show patch')),
2976 ('p', 'patch', None, _('show patch')),
2982 ('', 'template', '', _('display with template'))],
2977 ('', 'template', '', _('display with template'))],
2983 _('hg tip [-p]')),
2978 _('hg tip [-p]')),
2984 "unbundle":
2979 "unbundle":
2985 (unbundle,
2980 (unbundle,
2986 [('u', 'update', None,
2981 [('u', 'update', None,
2987 _('update to new tip if changesets were unbundled'))],
2982 _('update to new tip if changesets were unbundled'))],
2988 _('hg unbundle [-u] FILE')),
2983 _('hg unbundle [-u] FILE')),
2989 "^update|up|checkout|co":
2984 "^update|up|checkout|co":
2990 (update,
2985 (update,
2991 [('C', 'clean', None, _('overwrite locally modified files')),
2986 [('C', 'clean', None, _('overwrite locally modified files')),
2992 ('d', 'date', '', _('tipmost revision matching date'))],
2987 ('d', 'date', '', _('tipmost revision matching date'))],
2993 _('hg update [-C] [-d DATE] [REV]')),
2988 _('hg update [-C] [-d DATE] [REV]')),
2994 "verify": (verify, [], _('hg verify')),
2989 "verify": (verify, [], _('hg verify')),
2995 "version": (version_, [], _('hg version')),
2990 "version": (version_, [], _('hg version')),
2996 }
2991 }
2997
2992
2998 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2993 norepo = ("clone init version help debugancestor debugcomplete debugdata"
2999 " debugindex debugindexdot debugdate debuginstall")
2994 " debugindex debugindexdot debugdate debuginstall")
3000 optionalrepo = ("paths serve showconfig")
2995 optionalrepo = ("paths serve showconfig")
3001
2996
3002 def findpossible(ui, cmd):
2997 def findpossible(ui, cmd):
3003 """
2998 """
3004 Return cmd -> (aliases, command table entry)
2999 Return cmd -> (aliases, command table entry)
3005 for each matching command.
3000 for each matching command.
3006 Return debug commands (or their aliases) only if no normal command matches.
3001 Return debug commands (or their aliases) only if no normal command matches.
3007 """
3002 """
3008 choice = {}
3003 choice = {}
3009 debugchoice = {}
3004 debugchoice = {}
3010 for e in table.keys():
3005 for e in table.keys():
3011 aliases = e.lstrip("^").split("|")
3006 aliases = e.lstrip("^").split("|")
3012 found = None
3007 found = None
3013 if cmd in aliases:
3008 if cmd in aliases:
3014 found = cmd
3009 found = cmd
3015 elif not ui.config("ui", "strict"):
3010 elif not ui.config("ui", "strict"):
3016 for a in aliases:
3011 for a in aliases:
3017 if a.startswith(cmd):
3012 if a.startswith(cmd):
3018 found = a
3013 found = a
3019 break
3014 break
3020 if found is not None:
3015 if found is not None:
3021 if aliases[0].startswith("debug") or found.startswith("debug"):
3016 if aliases[0].startswith("debug") or found.startswith("debug"):
3022 debugchoice[found] = (aliases, table[e])
3017 debugchoice[found] = (aliases, table[e])
3023 else:
3018 else:
3024 choice[found] = (aliases, table[e])
3019 choice[found] = (aliases, table[e])
3025
3020
3026 if not choice and debugchoice:
3021 if not choice and debugchoice:
3027 choice = debugchoice
3022 choice = debugchoice
3028
3023
3029 return choice
3024 return choice
3030
3025
3031 def findcmd(ui, cmd):
3026 def findcmd(ui, cmd):
3032 """Return (aliases, command table entry) for command string."""
3027 """Return (aliases, command table entry) for command string."""
3033 choice = findpossible(ui, cmd)
3028 choice = findpossible(ui, cmd)
3034
3029
3035 if choice.has_key(cmd):
3030 if choice.has_key(cmd):
3036 return choice[cmd]
3031 return choice[cmd]
3037
3032
3038 if len(choice) > 1:
3033 if len(choice) > 1:
3039 clist = choice.keys()
3034 clist = choice.keys()
3040 clist.sort()
3035 clist.sort()
3041 raise AmbiguousCommand(cmd, clist)
3036 raise AmbiguousCommand(cmd, clist)
3042
3037
3043 if choice:
3038 if choice:
3044 return choice.values()[0]
3039 return choice.values()[0]
3045
3040
3046 raise UnknownCommand(cmd)
3041 raise UnknownCommand(cmd)
3047
3042
3048 def catchterm(*args):
3043 def catchterm(*args):
3049 raise util.SignalInterrupt
3044 raise util.SignalInterrupt
3050
3045
3051 def run():
3046 def run():
3052 sys.exit(dispatch(sys.argv[1:]))
3047 sys.exit(dispatch(sys.argv[1:]))
3053
3048
3054 class ParseError(Exception):
3049 class ParseError(Exception):
3055 """Exception raised on errors in parsing the command line."""
3050 """Exception raised on errors in parsing the command line."""
3056
3051
3057 def parse(ui, args):
3052 def parse(ui, args):
3058 options = {}
3053 options = {}
3059 cmdoptions = {}
3054 cmdoptions = {}
3060
3055
3061 try:
3056 try:
3062 args = fancyopts.fancyopts(args, globalopts, options)
3057 args = fancyopts.fancyopts(args, globalopts, options)
3063 except fancyopts.getopt.GetoptError, inst:
3058 except fancyopts.getopt.GetoptError, inst:
3064 raise ParseError(None, inst)
3059 raise ParseError(None, inst)
3065
3060
3066 if args:
3061 if args:
3067 cmd, args = args[0], args[1:]
3062 cmd, args = args[0], args[1:]
3068 aliases, i = findcmd(ui, cmd)
3063 aliases, i = findcmd(ui, cmd)
3069 cmd = aliases[0]
3064 cmd = aliases[0]
3070 defaults = ui.config("defaults", cmd)
3065 defaults = ui.config("defaults", cmd)
3071 if defaults:
3066 if defaults:
3072 args = shlex.split(defaults) + args
3067 args = shlex.split(defaults) + args
3073 c = list(i[1])
3068 c = list(i[1])
3074 else:
3069 else:
3075 cmd = None
3070 cmd = None
3076 c = []
3071 c = []
3077
3072
3078 # combine global options into local
3073 # combine global options into local
3079 for o in globalopts:
3074 for o in globalopts:
3080 c.append((o[0], o[1], options[o[1]], o[3]))
3075 c.append((o[0], o[1], options[o[1]], o[3]))
3081
3076
3082 try:
3077 try:
3083 args = fancyopts.fancyopts(args, c, cmdoptions)
3078 args = fancyopts.fancyopts(args, c, cmdoptions)
3084 except fancyopts.getopt.GetoptError, inst:
3079 except fancyopts.getopt.GetoptError, inst:
3085 raise ParseError(cmd, inst)
3080 raise ParseError(cmd, inst)
3086
3081
3087 # separate global options back out
3082 # separate global options back out
3088 for o in globalopts:
3083 for o in globalopts:
3089 n = o[1]
3084 n = o[1]
3090 options[n] = cmdoptions[n]
3085 options[n] = cmdoptions[n]
3091 del cmdoptions[n]
3086 del cmdoptions[n]
3092
3087
3093 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3088 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3094
3089
3095 external = {}
3090 external = {}
3096
3091
3097 def findext(name):
3092 def findext(name):
3098 '''return module with given extension name'''
3093 '''return module with given extension name'''
3099 try:
3094 try:
3100 return sys.modules[external[name]]
3095 return sys.modules[external[name]]
3101 except KeyError:
3096 except KeyError:
3102 for k, v in external.iteritems():
3097 for k, v in external.iteritems():
3103 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3098 if k.endswith('.' + name) or k.endswith('/' + name) or v == name:
3104 return sys.modules[v]
3099 return sys.modules[v]
3105 raise KeyError(name)
3100 raise KeyError(name)
3106
3101
3107 def load_extensions(ui):
3102 def load_extensions(ui):
3108 added = []
3103 added = []
3109 for ext_name, load_from_name in ui.extensions():
3104 for ext_name, load_from_name in ui.extensions():
3110 if ext_name in external:
3105 if ext_name in external:
3111 continue
3106 continue
3112 try:
3107 try:
3113 if load_from_name:
3108 if load_from_name:
3114 # the module will be loaded in sys.modules
3109 # the module will be loaded in sys.modules
3115 # choose an unique name so that it doesn't
3110 # choose an unique name so that it doesn't
3116 # conflicts with other modules
3111 # conflicts with other modules
3117 module_name = "hgext_%s" % ext_name.replace('.', '_')
3112 module_name = "hgext_%s" % ext_name.replace('.', '_')
3118 mod = imp.load_source(module_name, load_from_name)
3113 mod = imp.load_source(module_name, load_from_name)
3119 else:
3114 else:
3120 def importh(name):
3115 def importh(name):
3121 mod = __import__(name)
3116 mod = __import__(name)
3122 components = name.split('.')
3117 components = name.split('.')
3123 for comp in components[1:]:
3118 for comp in components[1:]:
3124 mod = getattr(mod, comp)
3119 mod = getattr(mod, comp)
3125 return mod
3120 return mod
3126 try:
3121 try:
3127 mod = importh("hgext.%s" % ext_name)
3122 mod = importh("hgext.%s" % ext_name)
3128 except ImportError:
3123 except ImportError:
3129 mod = importh(ext_name)
3124 mod = importh(ext_name)
3130 external[ext_name] = mod.__name__
3125 external[ext_name] = mod.__name__
3131 added.append((mod, ext_name))
3126 added.append((mod, ext_name))
3132 except (util.SignalInterrupt, KeyboardInterrupt):
3127 except (util.SignalInterrupt, KeyboardInterrupt):
3133 raise
3128 raise
3134 except Exception, inst:
3129 except Exception, inst:
3135 ui.warn(_("*** failed to import extension %s: %s\n") %
3130 ui.warn(_("*** failed to import extension %s: %s\n") %
3136 (ext_name, inst))
3131 (ext_name, inst))
3137 if ui.print_exc():
3132 if ui.print_exc():
3138 return 1
3133 return 1
3139
3134
3140 for mod, name in added:
3135 for mod, name in added:
3141 uisetup = getattr(mod, 'uisetup', None)
3136 uisetup = getattr(mod, 'uisetup', None)
3142 if uisetup:
3137 if uisetup:
3143 uisetup(ui)
3138 uisetup(ui)
3144 reposetup = getattr(mod, 'reposetup', None)
3139 reposetup = getattr(mod, 'reposetup', None)
3145 if reposetup:
3140 if reposetup:
3146 hg.repo_setup_hooks.append(reposetup)
3141 hg.repo_setup_hooks.append(reposetup)
3147 cmdtable = getattr(mod, 'cmdtable', {})
3142 cmdtable = getattr(mod, 'cmdtable', {})
3148 overrides = [cmd for cmd in cmdtable if cmd in table]
3143 overrides = [cmd for cmd in cmdtable if cmd in table]
3149 if overrides:
3144 if overrides:
3150 ui.warn(_("extension '%s' overrides commands: %s\n")
3145 ui.warn(_("extension '%s' overrides commands: %s\n")
3151 % (name, " ".join(overrides)))
3146 % (name, " ".join(overrides)))
3152 table.update(cmdtable)
3147 table.update(cmdtable)
3153
3148
3154 def parseconfig(config):
3149 def parseconfig(config):
3155 """parse the --config options from the command line"""
3150 """parse the --config options from the command line"""
3156 parsed = []
3151 parsed = []
3157 for cfg in config:
3152 for cfg in config:
3158 try:
3153 try:
3159 name, value = cfg.split('=', 1)
3154 name, value = cfg.split('=', 1)
3160 section, name = name.split('.', 1)
3155 section, name = name.split('.', 1)
3161 if not section or not name:
3156 if not section or not name:
3162 raise IndexError
3157 raise IndexError
3163 parsed.append((section, name, value))
3158 parsed.append((section, name, value))
3164 except (IndexError, ValueError):
3159 except (IndexError, ValueError):
3165 raise util.Abort(_('malformed --config option: %s') % cfg)
3160 raise util.Abort(_('malformed --config option: %s') % cfg)
3166 return parsed
3161 return parsed
3167
3162
3168 def dispatch(args):
3163 def dispatch(args):
3169 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3164 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3170 num = getattr(signal, name, None)
3165 num = getattr(signal, name, None)
3171 if num: signal.signal(num, catchterm)
3166 if num: signal.signal(num, catchterm)
3172
3167
3173 try:
3168 try:
3174 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3169 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3175 except util.Abort, inst:
3170 except util.Abort, inst:
3176 sys.stderr.write(_("abort: %s\n") % inst)
3171 sys.stderr.write(_("abort: %s\n") % inst)
3177 return -1
3172 return -1
3178
3173
3179 load_extensions(u)
3174 load_extensions(u)
3180 u.addreadhook(load_extensions)
3175 u.addreadhook(load_extensions)
3181
3176
3182 try:
3177 try:
3183 cmd, func, args, options, cmdoptions = parse(u, args)
3178 cmd, func, args, options, cmdoptions = parse(u, args)
3184 if options["encoding"]:
3179 if options["encoding"]:
3185 util._encoding = options["encoding"]
3180 util._encoding = options["encoding"]
3186 if options["encodingmode"]:
3181 if options["encodingmode"]:
3187 util._encodingmode = options["encodingmode"]
3182 util._encodingmode = options["encodingmode"]
3188 if options["time"]:
3183 if options["time"]:
3189 def get_times():
3184 def get_times():
3190 t = os.times()
3185 t = os.times()
3191 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3186 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3192 t = (t[0], t[1], t[2], t[3], time.clock())
3187 t = (t[0], t[1], t[2], t[3], time.clock())
3193 return t
3188 return t
3194 s = get_times()
3189 s = get_times()
3195 def print_time():
3190 def print_time():
3196 t = get_times()
3191 t = get_times()
3197 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3192 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3198 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3193 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3199 atexit.register(print_time)
3194 atexit.register(print_time)
3200
3195
3201 # enter the debugger before command execution
3196 # enter the debugger before command execution
3202 if options['debugger']:
3197 if options['debugger']:
3203 pdb.set_trace()
3198 pdb.set_trace()
3204
3199
3205 try:
3200 try:
3206 if options['cwd']:
3201 if options['cwd']:
3207 os.chdir(options['cwd'])
3202 os.chdir(options['cwd'])
3208
3203
3209 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3204 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3210 not options["noninteractive"], options["traceback"],
3205 not options["noninteractive"], options["traceback"],
3211 parseconfig(options["config"]))
3206 parseconfig(options["config"]))
3212
3207
3213 path = u.expandpath(options["repository"]) or ""
3208 path = u.expandpath(options["repository"]) or ""
3214 repo = path and hg.repository(u, path=path) or None
3209 repo = path and hg.repository(u, path=path) or None
3215 if repo and not repo.local():
3210 if repo and not repo.local():
3216 raise util.Abort(_("repository '%s' is not local") % path)
3211 raise util.Abort(_("repository '%s' is not local") % path)
3217
3212
3218 if options['help']:
3213 if options['help']:
3219 return help_(u, cmd, options['version'])
3214 return help_(u, cmd, options['version'])
3220 elif options['version']:
3215 elif options['version']:
3221 return version_(u)
3216 return version_(u)
3222 elif not cmd:
3217 elif not cmd:
3223 return help_(u, 'shortlist')
3218 return help_(u, 'shortlist')
3224
3219
3225 if cmd not in norepo.split():
3220 if cmd not in norepo.split():
3226 try:
3221 try:
3227 if not repo:
3222 if not repo:
3228 repo = hg.repository(u, path=path)
3223 repo = hg.repository(u, path=path)
3229 u = repo.ui
3224 u = repo.ui
3230 except hg.RepoError:
3225 except hg.RepoError:
3231 if cmd not in optionalrepo.split():
3226 if cmd not in optionalrepo.split():
3232 raise
3227 raise
3233 d = lambda: func(u, repo, *args, **cmdoptions)
3228 d = lambda: func(u, repo, *args, **cmdoptions)
3234 else:
3229 else:
3235 d = lambda: func(u, *args, **cmdoptions)
3230 d = lambda: func(u, *args, **cmdoptions)
3236
3231
3237 try:
3232 try:
3238 if options['profile']:
3233 if options['profile']:
3239 import hotshot, hotshot.stats
3234 import hotshot, hotshot.stats
3240 prof = hotshot.Profile("hg.prof")
3235 prof = hotshot.Profile("hg.prof")
3241 try:
3236 try:
3242 try:
3237 try:
3243 return prof.runcall(d)
3238 return prof.runcall(d)
3244 except:
3239 except:
3245 try:
3240 try:
3246 u.warn(_('exception raised - generating '
3241 u.warn(_('exception raised - generating '
3247 'profile anyway\n'))
3242 'profile anyway\n'))
3248 except:
3243 except:
3249 pass
3244 pass
3250 raise
3245 raise
3251 finally:
3246 finally:
3252 prof.close()
3247 prof.close()
3253 stats = hotshot.stats.load("hg.prof")
3248 stats = hotshot.stats.load("hg.prof")
3254 stats.strip_dirs()
3249 stats.strip_dirs()
3255 stats.sort_stats('time', 'calls')
3250 stats.sort_stats('time', 'calls')
3256 stats.print_stats(40)
3251 stats.print_stats(40)
3257 elif options['lsprof']:
3252 elif options['lsprof']:
3258 try:
3253 try:
3259 from mercurial import lsprof
3254 from mercurial import lsprof
3260 except ImportError:
3255 except ImportError:
3261 raise util.Abort(_(
3256 raise util.Abort(_(
3262 'lsprof not available - install from '
3257 'lsprof not available - install from '
3263 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3258 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3264 p = lsprof.Profiler()
3259 p = lsprof.Profiler()
3265 p.enable(subcalls=True)
3260 p.enable(subcalls=True)
3266 try:
3261 try:
3267 return d()
3262 return d()
3268 finally:
3263 finally:
3269 p.disable()
3264 p.disable()
3270 stats = lsprof.Stats(p.getstats())
3265 stats = lsprof.Stats(p.getstats())
3271 stats.sort()
3266 stats.sort()
3272 stats.pprint(top=10, file=sys.stderr, climit=5)
3267 stats.pprint(top=10, file=sys.stderr, climit=5)
3273 else:
3268 else:
3274 return d()
3269 return d()
3275 finally:
3270 finally:
3276 u.flush()
3271 u.flush()
3277 except:
3272 except:
3278 # enter the debugger when we hit an exception
3273 # enter the debugger when we hit an exception
3279 if options['debugger']:
3274 if options['debugger']:
3280 pdb.post_mortem(sys.exc_info()[2])
3275 pdb.post_mortem(sys.exc_info()[2])
3281 u.print_exc()
3276 u.print_exc()
3282 raise
3277 raise
3283 except ParseError, inst:
3278 except ParseError, inst:
3284 if inst.args[0]:
3279 if inst.args[0]:
3285 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3280 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3286 help_(u, inst.args[0])
3281 help_(u, inst.args[0])
3287 else:
3282 else:
3288 u.warn(_("hg: %s\n") % inst.args[1])
3283 u.warn(_("hg: %s\n") % inst.args[1])
3289 help_(u, 'shortlist')
3284 help_(u, 'shortlist')
3290 except AmbiguousCommand, inst:
3285 except AmbiguousCommand, inst:
3291 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3286 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3292 (inst.args[0], " ".join(inst.args[1])))
3287 (inst.args[0], " ".join(inst.args[1])))
3293 except UnknownCommand, inst:
3288 except UnknownCommand, inst:
3294 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3289 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3295 help_(u, 'shortlist')
3290 help_(u, 'shortlist')
3296 except hg.RepoError, inst:
3291 except hg.RepoError, inst:
3297 u.warn(_("abort: %s!\n") % inst)
3292 u.warn(_("abort: %s!\n") % inst)
3298 except lock.LockHeld, inst:
3293 except lock.LockHeld, inst:
3299 if inst.errno == errno.ETIMEDOUT:
3294 if inst.errno == errno.ETIMEDOUT:
3300 reason = _('timed out waiting for lock held by %s') % inst.locker
3295 reason = _('timed out waiting for lock held by %s') % inst.locker
3301 else:
3296 else:
3302 reason = _('lock held by %s') % inst.locker
3297 reason = _('lock held by %s') % inst.locker
3303 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3298 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3304 except lock.LockUnavailable, inst:
3299 except lock.LockUnavailable, inst:
3305 u.warn(_("abort: could not lock %s: %s\n") %
3300 u.warn(_("abort: could not lock %s: %s\n") %
3306 (inst.desc or inst.filename, inst.strerror))
3301 (inst.desc or inst.filename, inst.strerror))
3307 except revlog.RevlogError, inst:
3302 except revlog.RevlogError, inst:
3308 u.warn(_("abort: %s!\n") % inst)
3303 u.warn(_("abort: %s!\n") % inst)
3309 except util.SignalInterrupt:
3304 except util.SignalInterrupt:
3310 u.warn(_("killed!\n"))
3305 u.warn(_("killed!\n"))
3311 except KeyboardInterrupt:
3306 except KeyboardInterrupt:
3312 try:
3307 try:
3313 u.warn(_("interrupted!\n"))
3308 u.warn(_("interrupted!\n"))
3314 except IOError, inst:
3309 except IOError, inst:
3315 if inst.errno == errno.EPIPE:
3310 if inst.errno == errno.EPIPE:
3316 if u.debugflag:
3311 if u.debugflag:
3317 u.warn(_("\nbroken pipe\n"))
3312 u.warn(_("\nbroken pipe\n"))
3318 else:
3313 else:
3319 raise
3314 raise
3320 except socket.error, inst:
3315 except socket.error, inst:
3321 u.warn(_("abort: %s\n") % inst[1])
3316 u.warn(_("abort: %s\n") % inst[1])
3322 except IOError, inst:
3317 except IOError, inst:
3323 if hasattr(inst, "code"):
3318 if hasattr(inst, "code"):
3324 u.warn(_("abort: %s\n") % inst)
3319 u.warn(_("abort: %s\n") % inst)
3325 elif hasattr(inst, "reason"):
3320 elif hasattr(inst, "reason"):
3326 try: # usually it is in the form (errno, strerror)
3321 try: # usually it is in the form (errno, strerror)
3327 reason = inst.reason.args[1]
3322 reason = inst.reason.args[1]
3328 except: # it might be anything, for example a string
3323 except: # it might be anything, for example a string
3329 reason = inst.reason
3324 reason = inst.reason
3330 u.warn(_("abort: error: %s\n") % reason)
3325 u.warn(_("abort: error: %s\n") % reason)
3331 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3326 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3332 if u.debugflag:
3327 if u.debugflag:
3333 u.warn(_("broken pipe\n"))
3328 u.warn(_("broken pipe\n"))
3334 elif getattr(inst, "strerror", None):
3329 elif getattr(inst, "strerror", None):
3335 if getattr(inst, "filename", None):
3330 if getattr(inst, "filename", None):
3336 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3331 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3337 else:
3332 else:
3338 u.warn(_("abort: %s\n") % inst.strerror)
3333 u.warn(_("abort: %s\n") % inst.strerror)
3339 else:
3334 else:
3340 raise
3335 raise
3341 except OSError, inst:
3336 except OSError, inst:
3342 if getattr(inst, "filename", None):
3337 if getattr(inst, "filename", None):
3343 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3338 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3344 else:
3339 else:
3345 u.warn(_("abort: %s\n") % inst.strerror)
3340 u.warn(_("abort: %s\n") % inst.strerror)
3346 except util.UnexpectedOutput, inst:
3341 except util.UnexpectedOutput, inst:
3347 u.warn(_("abort: %s") % inst[0])
3342 u.warn(_("abort: %s") % inst[0])
3348 if not isinstance(inst[1], basestring):
3343 if not isinstance(inst[1], basestring):
3349 u.warn(" %r\n" % (inst[1],))
3344 u.warn(" %r\n" % (inst[1],))
3350 elif not inst[1]:
3345 elif not inst[1]:
3351 u.warn(_(" empty string\n"))
3346 u.warn(_(" empty string\n"))
3352 else:
3347 else:
3353 u.warn("\n%r\n" % util.ellipsis(inst[1]))
3348 u.warn("\n%r\n" % util.ellipsis(inst[1]))
3354 except util.Abort, inst:
3349 except util.Abort, inst:
3355 u.warn(_("abort: %s\n") % inst)
3350 u.warn(_("abort: %s\n") % inst)
3356 except TypeError, inst:
3351 except TypeError, inst:
3357 # was this an argument error?
3352 # was this an argument error?
3358 tb = traceback.extract_tb(sys.exc_info()[2])
3353 tb = traceback.extract_tb(sys.exc_info()[2])
3359 if len(tb) > 2: # no
3354 if len(tb) > 2: # no
3360 raise
3355 raise
3361 u.debug(inst, "\n")
3356 u.debug(inst, "\n")
3362 u.warn(_("%s: invalid arguments\n") % cmd)
3357 u.warn(_("%s: invalid arguments\n") % cmd)
3363 help_(u, cmd)
3358 help_(u, cmd)
3364 except SystemExit, inst:
3359 except SystemExit, inst:
3365 # Commands shouldn't sys.exit directly, but give a return code.
3360 # Commands shouldn't sys.exit directly, but give a return code.
3366 # Just in case catch this and and pass exit code to caller.
3361 # Just in case catch this and and pass exit code to caller.
3367 return inst.code
3362 return inst.code
3368 except:
3363 except:
3369 u.warn(_("** unknown exception encountered, details follow\n"))
3364 u.warn(_("** unknown exception encountered, details follow\n"))
3370 u.warn(_("** report bug details to "
3365 u.warn(_("** report bug details to "
3371 "http://www.selenic.com/mercurial/bts\n"))
3366 "http://www.selenic.com/mercurial/bts\n"))
3372 u.warn(_("** or mercurial@selenic.com\n"))
3367 u.warn(_("** or mercurial@selenic.com\n"))
3373 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3368 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3374 % version.get_version())
3369 % version.get_version())
3375 raise
3370 raise
3376
3371
3377 return -1
3372 return -1
@@ -1,666 +1,668 b''
1 # patch.py - patch file parsing routines
1 # patch.py - patch file parsing routines
2 #
2 #
3 # Copyright 2006 Brendan Cully <brendan@kublai.com>
3 # Copyright 2006 Brendan Cully <brendan@kublai.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 from i18n import _
8 from i18n import _
9 from node import *
9 from node import *
10 import base85, cmdutil, mdiff, util, context, revlog
10 import base85, cmdutil, mdiff, util, context, revlog
11 import cStringIO, email.Parser, os, popen2, re, sha
11 import cStringIO, email.Parser, os, popen2, re, sha
12 import sys, tempfile, zlib
12 import sys, tempfile, zlib
13
13
14 # helper functions
14 # helper functions
15
15
16 def copyfile(src, dst, basedir=None):
16 def copyfile(src, dst, basedir=None):
17 if not basedir:
17 if not basedir:
18 basedir = os.getcwd()
18 basedir = os.getcwd()
19
19
20 abssrc, absdst = [os.path.join(basedir, n) for n in (src, dst)]
20 abssrc, absdst = [os.path.join(basedir, n) for n in (src, dst)]
21 if os.path.exists(absdst):
21 if os.path.exists(absdst):
22 raise util.Abort(_("cannot create %s: destination already exists") %
22 raise util.Abort(_("cannot create %s: destination already exists") %
23 dst)
23 dst)
24
24
25 targetdir = os.path.dirname(absdst)
25 targetdir = os.path.dirname(absdst)
26 if not os.path.isdir(targetdir):
26 if not os.path.isdir(targetdir):
27 os.makedirs(targetdir)
27 os.makedirs(targetdir)
28
28
29 util.copyfile(abssrc, absdst)
29 util.copyfile(abssrc, absdst)
30
30
31 # public functions
31 # public functions
32
32
33 def extract(ui, fileobj):
33 def extract(ui, fileobj):
34 '''extract patch from data read from fileobj.
34 '''extract patch from data read from fileobj.
35
35
36 patch can be a normal patch or contained in an email message.
36 patch can be a normal patch or contained in an email message.
37
37
38 return tuple (filename, message, user, date, node, p1, p2).
38 return tuple (filename, message, user, date, node, p1, p2).
39 Any item in the returned tuple can be None. If filename is None,
39 Any item in the returned tuple can be None. If filename is None,
40 fileobj did not contain a patch. Caller must unlink filename when done.'''
40 fileobj did not contain a patch. Caller must unlink filename when done.'''
41
41
42 # attempt to detect the start of a patch
42 # attempt to detect the start of a patch
43 # (this heuristic is borrowed from quilt)
43 # (this heuristic is borrowed from quilt)
44 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
44 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
45 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
45 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
46 '(---|\*\*\*)[ \t])', re.MULTILINE)
46 '(---|\*\*\*)[ \t])', re.MULTILINE)
47
47
48 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
48 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
49 tmpfp = os.fdopen(fd, 'w')
49 tmpfp = os.fdopen(fd, 'w')
50 try:
50 try:
51 msg = email.Parser.Parser().parse(fileobj)
51 msg = email.Parser.Parser().parse(fileobj)
52
52
53 message = msg['Subject']
53 message = msg['Subject']
54 user = msg['From']
54 user = msg['From']
55 # should try to parse msg['Date']
55 # should try to parse msg['Date']
56 date = None
56 date = None
57 nodeid = None
57 nodeid = None
58 parents = []
58 parents = []
59
59
60 if message:
60 if message:
61 if message.startswith('[PATCH'):
61 if message.startswith('[PATCH'):
62 pend = message.find(']')
62 pend = message.find(']')
63 if pend >= 0:
63 if pend >= 0:
64 message = message[pend+1:].lstrip()
64 message = message[pend+1:].lstrip()
65 message = message.replace('\n\t', ' ')
65 message = message.replace('\n\t', ' ')
66 ui.debug('Subject: %s\n' % message)
66 ui.debug('Subject: %s\n' % message)
67 if user:
67 if user:
68 ui.debug('From: %s\n' % user)
68 ui.debug('From: %s\n' % user)
69 diffs_seen = 0
69 diffs_seen = 0
70 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
70 ok_types = ('text/plain', 'text/x-diff', 'text/x-patch')
71
71
72 for part in msg.walk():
72 for part in msg.walk():
73 content_type = part.get_content_type()
73 content_type = part.get_content_type()
74 ui.debug('Content-Type: %s\n' % content_type)
74 ui.debug('Content-Type: %s\n' % content_type)
75 if content_type not in ok_types:
75 if content_type not in ok_types:
76 continue
76 continue
77 payload = part.get_payload(decode=True)
77 payload = part.get_payload(decode=True)
78 m = diffre.search(payload)
78 m = diffre.search(payload)
79 if m:
79 if m:
80 hgpatch = False
80 hgpatch = False
81 ignoretext = False
81 ignoretext = False
82
82
83 ui.debug(_('found patch at byte %d\n') % m.start(0))
83 ui.debug(_('found patch at byte %d\n') % m.start(0))
84 diffs_seen += 1
84 diffs_seen += 1
85 cfp = cStringIO.StringIO()
85 cfp = cStringIO.StringIO()
86 if message:
86 if message:
87 cfp.write(message)
87 cfp.write(message)
88 cfp.write('\n')
88 cfp.write('\n')
89 for line in payload[:m.start(0)].splitlines():
89 for line in payload[:m.start(0)].splitlines():
90 if line.startswith('# HG changeset patch'):
90 if line.startswith('# HG changeset patch'):
91 ui.debug(_('patch generated by hg export\n'))
91 ui.debug(_('patch generated by hg export\n'))
92 hgpatch = True
92 hgpatch = True
93 # drop earlier commit message content
93 # drop earlier commit message content
94 cfp.seek(0)
94 cfp.seek(0)
95 cfp.truncate()
95 cfp.truncate()
96 elif hgpatch:
96 elif hgpatch:
97 if line.startswith('# User '):
97 if line.startswith('# User '):
98 user = line[7:]
98 user = line[7:]
99 ui.debug('From: %s\n' % user)
99 ui.debug('From: %s\n' % user)
100 elif line.startswith("# Date "):
100 elif line.startswith("# Date "):
101 date = line[7:]
101 date = line[7:]
102 elif line.startswith("# Node ID "):
102 elif line.startswith("# Node ID "):
103 nodeid = line[10:]
103 nodeid = line[10:]
104 elif line.startswith("# Parent "):
104 elif line.startswith("# Parent "):
105 parents.append(line[10:])
105 parents.append(line[10:])
106 elif line == '---' and 'git-send-email' in msg['X-Mailer']:
106 elif line == '---' and 'git-send-email' in msg['X-Mailer']:
107 ignoretext = True
107 ignoretext = True
108 if not line.startswith('# ') and not ignoretext:
108 if not line.startswith('# ') and not ignoretext:
109 cfp.write(line)
109 cfp.write(line)
110 cfp.write('\n')
110 cfp.write('\n')
111 message = cfp.getvalue()
111 message = cfp.getvalue()
112 if tmpfp:
112 if tmpfp:
113 tmpfp.write(payload)
113 tmpfp.write(payload)
114 if not payload.endswith('\n'):
114 if not payload.endswith('\n'):
115 tmpfp.write('\n')
115 tmpfp.write('\n')
116 elif not diffs_seen and message and content_type == 'text/plain':
116 elif not diffs_seen and message and content_type == 'text/plain':
117 message += '\n' + payload
117 message += '\n' + payload
118 except:
118 except:
119 tmpfp.close()
119 tmpfp.close()
120 os.unlink(tmpname)
120 os.unlink(tmpname)
121 raise
121 raise
122
122
123 tmpfp.close()
123 tmpfp.close()
124 if not diffs_seen:
124 if not diffs_seen:
125 os.unlink(tmpname)
125 os.unlink(tmpname)
126 return None, message, user, date, None, None, None
126 return None, message, user, date, None, None, None
127 p1 = parents and parents.pop(0) or None
127 p1 = parents and parents.pop(0) or None
128 p2 = parents and parents.pop(0) or None
128 p2 = parents and parents.pop(0) or None
129 return tmpname, message, user, date, nodeid, p1, p2
129 return tmpname, message, user, date, nodeid, p1, p2
130
130
131 GP_PATCH = 1 << 0 # we have to run patch
131 GP_PATCH = 1 << 0 # we have to run patch
132 GP_FILTER = 1 << 1 # there's some copy/rename operation
132 GP_FILTER = 1 << 1 # there's some copy/rename operation
133 GP_BINARY = 1 << 2 # there's a binary patch
133 GP_BINARY = 1 << 2 # there's a binary patch
134
134
135 def readgitpatch(patchname):
135 def readgitpatch(patchname):
136 """extract git-style metadata about patches from <patchname>"""
136 """extract git-style metadata about patches from <patchname>"""
137 class gitpatch:
137 class gitpatch:
138 "op is one of ADD, DELETE, RENAME, MODIFY or COPY"
138 "op is one of ADD, DELETE, RENAME, MODIFY or COPY"
139 def __init__(self, path):
139 def __init__(self, path):
140 self.path = path
140 self.path = path
141 self.oldpath = None
141 self.oldpath = None
142 self.mode = None
142 self.mode = None
143 self.op = 'MODIFY'
143 self.op = 'MODIFY'
144 self.copymod = False
144 self.copymod = False
145 self.lineno = 0
145 self.lineno = 0
146 self.binary = False
146 self.binary = False
147
147
148 # Filter patch for git information
148 # Filter patch for git information
149 gitre = re.compile('diff --git a/(.*) b/(.*)')
149 gitre = re.compile('diff --git a/(.*) b/(.*)')
150 pf = file(patchname)
150 pf = file(patchname)
151 gp = None
151 gp = None
152 gitpatches = []
152 gitpatches = []
153 # Can have a git patch with only metadata, causing patch to complain
153 # Can have a git patch with only metadata, causing patch to complain
154 dopatch = 0
154 dopatch = 0
155
155
156 lineno = 0
156 lineno = 0
157 for line in pf:
157 for line in pf:
158 lineno += 1
158 lineno += 1
159 if line.startswith('diff --git'):
159 if line.startswith('diff --git'):
160 m = gitre.match(line)
160 m = gitre.match(line)
161 if m:
161 if m:
162 if gp:
162 if gp:
163 gitpatches.append(gp)
163 gitpatches.append(gp)
164 src, dst = m.group(1, 2)
164 src, dst = m.group(1, 2)
165 gp = gitpatch(dst)
165 gp = gitpatch(dst)
166 gp.lineno = lineno
166 gp.lineno = lineno
167 elif gp:
167 elif gp:
168 if line.startswith('--- '):
168 if line.startswith('--- '):
169 if gp.op in ('COPY', 'RENAME'):
169 if gp.op in ('COPY', 'RENAME'):
170 gp.copymod = True
170 gp.copymod = True
171 dopatch |= GP_FILTER
171 dopatch |= GP_FILTER
172 gitpatches.append(gp)
172 gitpatches.append(gp)
173 gp = None
173 gp = None
174 dopatch |= GP_PATCH
174 dopatch |= GP_PATCH
175 continue
175 continue
176 if line.startswith('rename from '):
176 if line.startswith('rename from '):
177 gp.op = 'RENAME'
177 gp.op = 'RENAME'
178 gp.oldpath = line[12:].rstrip()
178 gp.oldpath = line[12:].rstrip()
179 elif line.startswith('rename to '):
179 elif line.startswith('rename to '):
180 gp.path = line[10:].rstrip()
180 gp.path = line[10:].rstrip()
181 elif line.startswith('copy from '):
181 elif line.startswith('copy from '):
182 gp.op = 'COPY'
182 gp.op = 'COPY'
183 gp.oldpath = line[10:].rstrip()
183 gp.oldpath = line[10:].rstrip()
184 elif line.startswith('copy to '):
184 elif line.startswith('copy to '):
185 gp.path = line[8:].rstrip()
185 gp.path = line[8:].rstrip()
186 elif line.startswith('deleted file'):
186 elif line.startswith('deleted file'):
187 gp.op = 'DELETE'
187 gp.op = 'DELETE'
188 elif line.startswith('new file mode '):
188 elif line.startswith('new file mode '):
189 gp.op = 'ADD'
189 gp.op = 'ADD'
190 gp.mode = int(line.rstrip()[-3:], 8)
190 gp.mode = int(line.rstrip()[-3:], 8)
191 elif line.startswith('new mode '):
191 elif line.startswith('new mode '):
192 gp.mode = int(line.rstrip()[-3:], 8)
192 gp.mode = int(line.rstrip()[-3:], 8)
193 elif line.startswith('GIT binary patch'):
193 elif line.startswith('GIT binary patch'):
194 dopatch |= GP_BINARY
194 dopatch |= GP_BINARY
195 gp.binary = True
195 gp.binary = True
196 if gp:
196 if gp:
197 gitpatches.append(gp)
197 gitpatches.append(gp)
198
198
199 if not gitpatches:
199 if not gitpatches:
200 dopatch = GP_PATCH
200 dopatch = GP_PATCH
201
201
202 return (dopatch, gitpatches)
202 return (dopatch, gitpatches)
203
203
204 def dogitpatch(patchname, gitpatches, cwd=None):
204 def dogitpatch(patchname, gitpatches, cwd=None):
205 """Preprocess git patch so that vanilla patch can handle it"""
205 """Preprocess git patch so that vanilla patch can handle it"""
206 def extractbin(fp):
206 def extractbin(fp):
207 i = [0] # yuck
207 i = [0] # yuck
208 def readline():
208 def readline():
209 i[0] += 1
209 i[0] += 1
210 return fp.readline().rstrip()
210 return fp.readline().rstrip()
211 line = readline()
211 line = readline()
212 while line and not line.startswith('literal '):
212 while line and not line.startswith('literal '):
213 line = readline()
213 line = readline()
214 if not line:
214 if not line:
215 return None, i[0]
215 return None, i[0]
216 size = int(line[8:])
216 size = int(line[8:])
217 dec = []
217 dec = []
218 line = readline()
218 line = readline()
219 while line:
219 while line:
220 l = line[0]
220 l = line[0]
221 if l <= 'Z' and l >= 'A':
221 if l <= 'Z' and l >= 'A':
222 l = ord(l) - ord('A') + 1
222 l = ord(l) - ord('A') + 1
223 else:
223 else:
224 l = ord(l) - ord('a') + 27
224 l = ord(l) - ord('a') + 27
225 dec.append(base85.b85decode(line[1:])[:l])
225 dec.append(base85.b85decode(line[1:])[:l])
226 line = readline()
226 line = readline()
227 text = zlib.decompress(''.join(dec))
227 text = zlib.decompress(''.join(dec))
228 if len(text) != size:
228 if len(text) != size:
229 raise util.Abort(_('binary patch is %d bytes, not %d') %
229 raise util.Abort(_('binary patch is %d bytes, not %d') %
230 (len(text), size))
230 (len(text), size))
231 return text, i[0]
231 return text, i[0]
232
232
233 pf = file(patchname)
233 pf = file(patchname)
234 pfline = 1
234 pfline = 1
235
235
236 fd, patchname = tempfile.mkstemp(prefix='hg-patch-')
236 fd, patchname = tempfile.mkstemp(prefix='hg-patch-')
237 tmpfp = os.fdopen(fd, 'w')
237 tmpfp = os.fdopen(fd, 'w')
238
238
239 try:
239 try:
240 for i in xrange(len(gitpatches)):
240 for i in xrange(len(gitpatches)):
241 p = gitpatches[i]
241 p = gitpatches[i]
242 if not p.copymod and not p.binary:
242 if not p.copymod and not p.binary:
243 continue
243 continue
244
244
245 # rewrite patch hunk
245 # rewrite patch hunk
246 while pfline < p.lineno:
246 while pfline < p.lineno:
247 tmpfp.write(pf.readline())
247 tmpfp.write(pf.readline())
248 pfline += 1
248 pfline += 1
249
249
250 if p.binary:
250 if p.binary:
251 text, delta = extractbin(pf)
251 text, delta = extractbin(pf)
252 if not text:
252 if not text:
253 raise util.Abort(_('binary patch extraction failed'))
253 raise util.Abort(_('binary patch extraction failed'))
254 pfline += delta
254 pfline += delta
255 if not cwd:
255 if not cwd:
256 cwd = os.getcwd()
256 cwd = os.getcwd()
257 absdst = os.path.join(cwd, p.path)
257 absdst = os.path.join(cwd, p.path)
258 basedir = os.path.dirname(absdst)
258 basedir = os.path.dirname(absdst)
259 if not os.path.isdir(basedir):
259 if not os.path.isdir(basedir):
260 os.makedirs(basedir)
260 os.makedirs(basedir)
261 out = file(absdst, 'wb')
261 out = file(absdst, 'wb')
262 out.write(text)
262 out.write(text)
263 out.close()
263 out.close()
264 elif p.copymod:
264 elif p.copymod:
265 copyfile(p.oldpath, p.path, basedir=cwd)
265 copyfile(p.oldpath, p.path, basedir=cwd)
266 tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path))
266 tmpfp.write('diff --git a/%s b/%s\n' % (p.path, p.path))
267 line = pf.readline()
267 line = pf.readline()
268 pfline += 1
268 pfline += 1
269 while not line.startswith('--- a/'):
269 while not line.startswith('--- a/'):
270 tmpfp.write(line)
270 tmpfp.write(line)
271 line = pf.readline()
271 line = pf.readline()
272 pfline += 1
272 pfline += 1
273 tmpfp.write('--- a/%s\n' % p.path)
273 tmpfp.write('--- a/%s\n' % p.path)
274
274
275 line = pf.readline()
275 line = pf.readline()
276 while line:
276 while line:
277 tmpfp.write(line)
277 tmpfp.write(line)
278 line = pf.readline()
278 line = pf.readline()
279 except:
279 except:
280 tmpfp.close()
280 tmpfp.close()
281 os.unlink(patchname)
281 os.unlink(patchname)
282 raise
282 raise
283
283
284 tmpfp.close()
284 tmpfp.close()
285 return patchname
285 return patchname
286
286
287 def patch(patchname, ui, strip=1, cwd=None, files={}):
287 def patch(patchname, ui, strip=1, cwd=None, files={}):
288 """apply the patch <patchname> to the working directory.
288 """apply the patch <patchname> to the working directory.
289 a list of patched files is returned"""
289 a list of patched files is returned"""
290
290
291 # helper function
291 # helper function
292 def __patch(patchname):
292 def __patch(patchname):
293 """patch and updates the files and fuzz variables"""
293 """patch and updates the files and fuzz variables"""
294 fuzz = False
294 fuzz = False
295
295
296 args = []
296 args = []
297 patcher = ui.config('ui', 'patch')
297 patcher = ui.config('ui', 'patch')
298 patcher = ((patcher and util.find_exe(patcher)) or
299 util.find_exe('gpatch') or
300 util.find_exe('patch'))
298 if not patcher:
301 if not patcher:
299 patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''),
302 raise util.Abort(_('no patch command found in hgrc or PATH'))
300 'patch')
303 if util.needbinarypatch():
301 if util.needbinarypatch():
304 args.append('--binary')
302 args.append('--binary')
303
305
304 if cwd:
306 if cwd:
305 args.append('-d %s' % util.shellquote(cwd))
307 args.append('-d %s' % util.shellquote(cwd))
306 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
308 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
307 util.shellquote(patchname)))
309 util.shellquote(patchname)))
308
310
309 for line in fp:
311 for line in fp:
310 line = line.rstrip()
312 line = line.rstrip()
311 ui.note(line + '\n')
313 ui.note(line + '\n')
312 if line.startswith('patching file '):
314 if line.startswith('patching file '):
313 pf = util.parse_patch_output(line)
315 pf = util.parse_patch_output(line)
314 printed_file = False
316 printed_file = False
315 files.setdefault(pf, (None, None))
317 files.setdefault(pf, (None, None))
316 elif line.find('with fuzz') >= 0:
318 elif line.find('with fuzz') >= 0:
317 fuzz = True
319 fuzz = True
318 if not printed_file:
320 if not printed_file:
319 ui.warn(pf + '\n')
321 ui.warn(pf + '\n')
320 printed_file = True
322 printed_file = True
321 ui.warn(line + '\n')
323 ui.warn(line + '\n')
322 elif line.find('saving rejects to file') >= 0:
324 elif line.find('saving rejects to file') >= 0:
323 ui.warn(line + '\n')
325 ui.warn(line + '\n')
324 elif line.find('FAILED') >= 0:
326 elif line.find('FAILED') >= 0:
325 if not printed_file:
327 if not printed_file:
326 ui.warn(pf + '\n')
328 ui.warn(pf + '\n')
327 printed_file = True
329 printed_file = True
328 ui.warn(line + '\n')
330 ui.warn(line + '\n')
329 code = fp.close()
331 code = fp.close()
330 if code:
332 if code:
331 raise util.Abort(_("patch command failed: %s") %
333 raise util.Abort(_("patch command failed: %s") %
332 util.explain_exit(code)[0])
334 util.explain_exit(code)[0])
333 return fuzz
335 return fuzz
334
336
335 (dopatch, gitpatches) = readgitpatch(patchname)
337 (dopatch, gitpatches) = readgitpatch(patchname)
336 for gp in gitpatches:
338 for gp in gitpatches:
337 files[gp.path] = (gp.op, gp)
339 files[gp.path] = (gp.op, gp)
338
340
339 fuzz = False
341 fuzz = False
340 if dopatch:
342 if dopatch:
341 filterpatch = dopatch & (GP_FILTER | GP_BINARY)
343 filterpatch = dopatch & (GP_FILTER | GP_BINARY)
342 if filterpatch:
344 if filterpatch:
343 patchname = dogitpatch(patchname, gitpatches, cwd=cwd)
345 patchname = dogitpatch(patchname, gitpatches, cwd=cwd)
344 try:
346 try:
345 if dopatch & GP_PATCH:
347 if dopatch & GP_PATCH:
346 fuzz = __patch(patchname)
348 fuzz = __patch(patchname)
347 finally:
349 finally:
348 if filterpatch:
350 if filterpatch:
349 os.unlink(patchname)
351 os.unlink(patchname)
350
352
351 return fuzz
353 return fuzz
352
354
353 def diffopts(ui, opts={}, untrusted=False):
355 def diffopts(ui, opts={}, untrusted=False):
354 def get(key, name=None):
356 def get(key, name=None):
355 return (opts.get(key) or
357 return (opts.get(key) or
356 ui.configbool('diff', name or key, None, untrusted=untrusted))
358 ui.configbool('diff', name or key, None, untrusted=untrusted))
357 return mdiff.diffopts(
359 return mdiff.diffopts(
358 text=opts.get('text'),
360 text=opts.get('text'),
359 git=get('git'),
361 git=get('git'),
360 nodates=get('nodates'),
362 nodates=get('nodates'),
361 showfunc=get('show_function', 'showfunc'),
363 showfunc=get('show_function', 'showfunc'),
362 ignorews=get('ignore_all_space', 'ignorews'),
364 ignorews=get('ignore_all_space', 'ignorews'),
363 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
365 ignorewsamount=get('ignore_space_change', 'ignorewsamount'),
364 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'))
366 ignoreblanklines=get('ignore_blank_lines', 'ignoreblanklines'))
365
367
366 def updatedir(ui, repo, patches, wlock=None):
368 def updatedir(ui, repo, patches, wlock=None):
367 '''Update dirstate after patch application according to metadata'''
369 '''Update dirstate after patch application according to metadata'''
368 if not patches:
370 if not patches:
369 return
371 return
370 copies = []
372 copies = []
371 removes = {}
373 removes = {}
372 cfiles = patches.keys()
374 cfiles = patches.keys()
373 cwd = repo.getcwd()
375 cwd = repo.getcwd()
374 if cwd:
376 if cwd:
375 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
377 cfiles = [util.pathto(repo.root, cwd, f) for f in patches.keys()]
376 for f in patches:
378 for f in patches:
377 ctype, gp = patches[f]
379 ctype, gp = patches[f]
378 if ctype == 'RENAME':
380 if ctype == 'RENAME':
379 copies.append((gp.oldpath, gp.path, gp.copymod))
381 copies.append((gp.oldpath, gp.path, gp.copymod))
380 removes[gp.oldpath] = 1
382 removes[gp.oldpath] = 1
381 elif ctype == 'COPY':
383 elif ctype == 'COPY':
382 copies.append((gp.oldpath, gp.path, gp.copymod))
384 copies.append((gp.oldpath, gp.path, gp.copymod))
383 elif ctype == 'DELETE':
385 elif ctype == 'DELETE':
384 removes[gp.path] = 1
386 removes[gp.path] = 1
385 for src, dst, after in copies:
387 for src, dst, after in copies:
386 if not after:
388 if not after:
387 copyfile(src, dst, repo.root)
389 copyfile(src, dst, repo.root)
388 repo.copy(src, dst, wlock=wlock)
390 repo.copy(src, dst, wlock=wlock)
389 removes = removes.keys()
391 removes = removes.keys()
390 if removes:
392 if removes:
391 removes.sort()
393 removes.sort()
392 repo.remove(removes, True, wlock=wlock)
394 repo.remove(removes, True, wlock=wlock)
393 for f in patches:
395 for f in patches:
394 ctype, gp = patches[f]
396 ctype, gp = patches[f]
395 if gp and gp.mode:
397 if gp and gp.mode:
396 x = gp.mode & 0100 != 0
398 x = gp.mode & 0100 != 0
397 dst = os.path.join(repo.root, gp.path)
399 dst = os.path.join(repo.root, gp.path)
398 # patch won't create empty files
400 # patch won't create empty files
399 if ctype == 'ADD' and not os.path.exists(dst):
401 if ctype == 'ADD' and not os.path.exists(dst):
400 repo.wwrite(gp.path, '', x and 'x' or '')
402 repo.wwrite(gp.path, '', x and 'x' or '')
401 else:
403 else:
402 util.set_exec(dst, x)
404 util.set_exec(dst, x)
403 cmdutil.addremove(repo, cfiles, wlock=wlock)
405 cmdutil.addremove(repo, cfiles, wlock=wlock)
404 files = patches.keys()
406 files = patches.keys()
405 files.extend([r for r in removes if r not in files])
407 files.extend([r for r in removes if r not in files])
406 files.sort()
408 files.sort()
407
409
408 return files
410 return files
409
411
410 def b85diff(fp, to, tn):
412 def b85diff(fp, to, tn):
411 '''print base85-encoded binary diff'''
413 '''print base85-encoded binary diff'''
412 def gitindex(text):
414 def gitindex(text):
413 if not text:
415 if not text:
414 return '0' * 40
416 return '0' * 40
415 l = len(text)
417 l = len(text)
416 s = sha.new('blob %d\0' % l)
418 s = sha.new('blob %d\0' % l)
417 s.update(text)
419 s.update(text)
418 return s.hexdigest()
420 return s.hexdigest()
419
421
420 def fmtline(line):
422 def fmtline(line):
421 l = len(line)
423 l = len(line)
422 if l <= 26:
424 if l <= 26:
423 l = chr(ord('A') + l - 1)
425 l = chr(ord('A') + l - 1)
424 else:
426 else:
425 l = chr(l - 26 + ord('a') - 1)
427 l = chr(l - 26 + ord('a') - 1)
426 return '%c%s\n' % (l, base85.b85encode(line, True))
428 return '%c%s\n' % (l, base85.b85encode(line, True))
427
429
428 def chunk(text, csize=52):
430 def chunk(text, csize=52):
429 l = len(text)
431 l = len(text)
430 i = 0
432 i = 0
431 while i < l:
433 while i < l:
432 yield text[i:i+csize]
434 yield text[i:i+csize]
433 i += csize
435 i += csize
434
436
435 tohash = gitindex(to)
437 tohash = gitindex(to)
436 tnhash = gitindex(tn)
438 tnhash = gitindex(tn)
437 if tohash == tnhash:
439 if tohash == tnhash:
438 return ""
440 return ""
439
441
440 # TODO: deltas
442 # TODO: deltas
441 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
443 ret = ['index %s..%s\nGIT binary patch\nliteral %s\n' %
442 (tohash, tnhash, len(tn))]
444 (tohash, tnhash, len(tn))]
443 for l in chunk(zlib.compress(tn)):
445 for l in chunk(zlib.compress(tn)):
444 ret.append(fmtline(l))
446 ret.append(fmtline(l))
445 ret.append('\n')
447 ret.append('\n')
446 return ''.join(ret)
448 return ''.join(ret)
447
449
448 def diff(repo, node1=None, node2=None, files=None, match=util.always,
450 def diff(repo, node1=None, node2=None, files=None, match=util.always,
449 fp=None, changes=None, opts=None):
451 fp=None, changes=None, opts=None):
450 '''print diff of changes to files between two nodes, or node and
452 '''print diff of changes to files between two nodes, or node and
451 working directory.
453 working directory.
452
454
453 if node1 is None, use first dirstate parent instead.
455 if node1 is None, use first dirstate parent instead.
454 if node2 is None, compare node1 with working directory.'''
456 if node2 is None, compare node1 with working directory.'''
455
457
456 if opts is None:
458 if opts is None:
457 opts = mdiff.defaultopts
459 opts = mdiff.defaultopts
458 if fp is None:
460 if fp is None:
459 fp = repo.ui
461 fp = repo.ui
460
462
461 if not node1:
463 if not node1:
462 node1 = repo.dirstate.parents()[0]
464 node1 = repo.dirstate.parents()[0]
463
465
464 ccache = {}
466 ccache = {}
465 def getctx(r):
467 def getctx(r):
466 if r not in ccache:
468 if r not in ccache:
467 ccache[r] = context.changectx(repo, r)
469 ccache[r] = context.changectx(repo, r)
468 return ccache[r]
470 return ccache[r]
469
471
470 flcache = {}
472 flcache = {}
471 def getfilectx(f, ctx):
473 def getfilectx(f, ctx):
472 flctx = ctx.filectx(f, filelog=flcache.get(f))
474 flctx = ctx.filectx(f, filelog=flcache.get(f))
473 if f not in flcache:
475 if f not in flcache:
474 flcache[f] = flctx._filelog
476 flcache[f] = flctx._filelog
475 return flctx
477 return flctx
476
478
477 # reading the data for node1 early allows it to play nicely
479 # reading the data for node1 early allows it to play nicely
478 # with repo.status and the revlog cache.
480 # with repo.status and the revlog cache.
479 ctx1 = context.changectx(repo, node1)
481 ctx1 = context.changectx(repo, node1)
480 # force manifest reading
482 # force manifest reading
481 man1 = ctx1.manifest()
483 man1 = ctx1.manifest()
482 date1 = util.datestr(ctx1.date())
484 date1 = util.datestr(ctx1.date())
483
485
484 if not changes:
486 if not changes:
485 changes = repo.status(node1, node2, files, match=match)[:5]
487 changes = repo.status(node1, node2, files, match=match)[:5]
486 modified, added, removed, deleted, unknown = changes
488 modified, added, removed, deleted, unknown = changes
487
489
488 if not modified and not added and not removed:
490 if not modified and not added and not removed:
489 return
491 return
490
492
491 if node2:
493 if node2:
492 ctx2 = context.changectx(repo, node2)
494 ctx2 = context.changectx(repo, node2)
493 else:
495 else:
494 ctx2 = context.workingctx(repo)
496 ctx2 = context.workingctx(repo)
495 man2 = ctx2.manifest()
497 man2 = ctx2.manifest()
496
498
497 # returns False if there was no rename between ctx1 and ctx2
499 # returns False if there was no rename between ctx1 and ctx2
498 # returns None if the file was created between ctx1 and ctx2
500 # returns None if the file was created between ctx1 and ctx2
499 # returns the (file, node) present in ctx1 that was renamed to f in ctx2
501 # returns the (file, node) present in ctx1 that was renamed to f in ctx2
500 def renamed(f):
502 def renamed(f):
501 startrev = ctx1.rev()
503 startrev = ctx1.rev()
502 c = ctx2
504 c = ctx2
503 crev = c.rev()
505 crev = c.rev()
504 if crev is None:
506 if crev is None:
505 crev = repo.changelog.count()
507 crev = repo.changelog.count()
506 orig = f
508 orig = f
507 while crev > startrev:
509 while crev > startrev:
508 if f in c.files():
510 if f in c.files():
509 try:
511 try:
510 src = getfilectx(f, c).renamed()
512 src = getfilectx(f, c).renamed()
511 except revlog.LookupError:
513 except revlog.LookupError:
512 return None
514 return None
513 if src:
515 if src:
514 f = src[0]
516 f = src[0]
515 crev = c.parents()[0].rev()
517 crev = c.parents()[0].rev()
516 # try to reuse
518 # try to reuse
517 c = getctx(crev)
519 c = getctx(crev)
518 if f not in man1:
520 if f not in man1:
519 return None
521 return None
520 if f == orig:
522 if f == orig:
521 return False
523 return False
522 return f
524 return f
523
525
524 if repo.ui.quiet:
526 if repo.ui.quiet:
525 r = None
527 r = None
526 else:
528 else:
527 hexfunc = repo.ui.debugflag and hex or short
529 hexfunc = repo.ui.debugflag and hex or short
528 r = [hexfunc(node) for node in [node1, node2] if node]
530 r = [hexfunc(node) for node in [node1, node2] if node]
529
531
530 if opts.git:
532 if opts.git:
531 copied = {}
533 copied = {}
532 for f in added:
534 for f in added:
533 src = renamed(f)
535 src = renamed(f)
534 if src:
536 if src:
535 copied[f] = src
537 copied[f] = src
536 srcs = [x[1] for x in copied.items()]
538 srcs = [x[1] for x in copied.items()]
537
539
538 all = modified + added + removed
540 all = modified + added + removed
539 all.sort()
541 all.sort()
540 gone = {}
542 gone = {}
541
543
542 for f in all:
544 for f in all:
543 to = None
545 to = None
544 tn = None
546 tn = None
545 dodiff = True
547 dodiff = True
546 header = []
548 header = []
547 if f in man1:
549 if f in man1:
548 to = getfilectx(f, ctx1).data()
550 to = getfilectx(f, ctx1).data()
549 if f not in removed:
551 if f not in removed:
550 tn = getfilectx(f, ctx2).data()
552 tn = getfilectx(f, ctx2).data()
551 if opts.git:
553 if opts.git:
552 def gitmode(x):
554 def gitmode(x):
553 return x and '100755' or '100644'
555 return x and '100755' or '100644'
554 def addmodehdr(header, omode, nmode):
556 def addmodehdr(header, omode, nmode):
555 if omode != nmode:
557 if omode != nmode:
556 header.append('old mode %s\n' % omode)
558 header.append('old mode %s\n' % omode)
557 header.append('new mode %s\n' % nmode)
559 header.append('new mode %s\n' % nmode)
558
560
559 a, b = f, f
561 a, b = f, f
560 if f in added:
562 if f in added:
561 mode = gitmode(man2.execf(f))
563 mode = gitmode(man2.execf(f))
562 if f in copied:
564 if f in copied:
563 a = copied[f]
565 a = copied[f]
564 omode = gitmode(man1.execf(a))
566 omode = gitmode(man1.execf(a))
565 addmodehdr(header, omode, mode)
567 addmodehdr(header, omode, mode)
566 if a in removed and a not in gone:
568 if a in removed and a not in gone:
567 op = 'rename'
569 op = 'rename'
568 gone[a] = 1
570 gone[a] = 1
569 else:
571 else:
570 op = 'copy'
572 op = 'copy'
571 header.append('%s from %s\n' % (op, a))
573 header.append('%s from %s\n' % (op, a))
572 header.append('%s to %s\n' % (op, f))
574 header.append('%s to %s\n' % (op, f))
573 to = getfilectx(a, ctx1).data()
575 to = getfilectx(a, ctx1).data()
574 else:
576 else:
575 header.append('new file mode %s\n' % mode)
577 header.append('new file mode %s\n' % mode)
576 if util.binary(tn):
578 if util.binary(tn):
577 dodiff = 'binary'
579 dodiff = 'binary'
578 elif f in removed:
580 elif f in removed:
579 if f in srcs:
581 if f in srcs:
580 dodiff = False
582 dodiff = False
581 else:
583 else:
582 mode = gitmode(man1.execf(f))
584 mode = gitmode(man1.execf(f))
583 header.append('deleted file mode %s\n' % mode)
585 header.append('deleted file mode %s\n' % mode)
584 else:
586 else:
585 omode = gitmode(man1.execf(f))
587 omode = gitmode(man1.execf(f))
586 nmode = gitmode(man2.execf(f))
588 nmode = gitmode(man2.execf(f))
587 addmodehdr(header, omode, nmode)
589 addmodehdr(header, omode, nmode)
588 if util.binary(to) or util.binary(tn):
590 if util.binary(to) or util.binary(tn):
589 dodiff = 'binary'
591 dodiff = 'binary'
590 r = None
592 r = None
591 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
593 header.insert(0, 'diff --git a/%s b/%s\n' % (a, b))
592 if dodiff:
594 if dodiff:
593 if dodiff == 'binary':
595 if dodiff == 'binary':
594 text = b85diff(fp, to, tn)
596 text = b85diff(fp, to, tn)
595 else:
597 else:
596 text = mdiff.unidiff(to, date1,
598 text = mdiff.unidiff(to, date1,
597 # ctx2 date may be dynamic
599 # ctx2 date may be dynamic
598 tn, util.datestr(ctx2.date()),
600 tn, util.datestr(ctx2.date()),
599 f, r, opts=opts)
601 f, r, opts=opts)
600 if text or len(header) > 1:
602 if text or len(header) > 1:
601 fp.write(''.join(header))
603 fp.write(''.join(header))
602 fp.write(text)
604 fp.write(text)
603
605
604 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
606 def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
605 opts=None):
607 opts=None):
606 '''export changesets as hg patches.'''
608 '''export changesets as hg patches.'''
607
609
608 total = len(revs)
610 total = len(revs)
609 revwidth = max([len(str(rev)) for rev in revs])
611 revwidth = max([len(str(rev)) for rev in revs])
610
612
611 def single(rev, seqno, fp):
613 def single(rev, seqno, fp):
612 ctx = repo.changectx(rev)
614 ctx = repo.changectx(rev)
613 node = ctx.node()
615 node = ctx.node()
614 parents = [p.node() for p in ctx.parents() if p]
616 parents = [p.node() for p in ctx.parents() if p]
615 branch = ctx.branch()
617 branch = ctx.branch()
616 if switch_parent:
618 if switch_parent:
617 parents.reverse()
619 parents.reverse()
618 prev = (parents and parents[0]) or nullid
620 prev = (parents and parents[0]) or nullid
619
621
620 if not fp:
622 if not fp:
621 fp = cmdutil.make_file(repo, template, node, total=total,
623 fp = cmdutil.make_file(repo, template, node, total=total,
622 seqno=seqno, revwidth=revwidth)
624 seqno=seqno, revwidth=revwidth)
623 if fp != sys.stdout and hasattr(fp, 'name'):
625 if fp != sys.stdout and hasattr(fp, 'name'):
624 repo.ui.note("%s\n" % fp.name)
626 repo.ui.note("%s\n" % fp.name)
625
627
626 fp.write("# HG changeset patch\n")
628 fp.write("# HG changeset patch\n")
627 fp.write("# User %s\n" % ctx.user())
629 fp.write("# User %s\n" % ctx.user())
628 fp.write("# Date %d %d\n" % ctx.date())
630 fp.write("# Date %d %d\n" % ctx.date())
629 if branch and (branch != 'default'):
631 if branch and (branch != 'default'):
630 fp.write("# Branch %s\n" % branch)
632 fp.write("# Branch %s\n" % branch)
631 fp.write("# Node ID %s\n" % hex(node))
633 fp.write("# Node ID %s\n" % hex(node))
632 fp.write("# Parent %s\n" % hex(prev))
634 fp.write("# Parent %s\n" % hex(prev))
633 if len(parents) > 1:
635 if len(parents) > 1:
634 fp.write("# Parent %s\n" % hex(parents[1]))
636 fp.write("# Parent %s\n" % hex(parents[1]))
635 fp.write(ctx.description().rstrip())
637 fp.write(ctx.description().rstrip())
636 fp.write("\n\n")
638 fp.write("\n\n")
637
639
638 diff(repo, prev, node, fp=fp, opts=opts)
640 diff(repo, prev, node, fp=fp, opts=opts)
639 if fp not in (sys.stdout, repo.ui):
641 if fp not in (sys.stdout, repo.ui):
640 fp.close()
642 fp.close()
641
643
642 for seqno, rev in enumerate(revs):
644 for seqno, rev in enumerate(revs):
643 single(rev, seqno+1, fp)
645 single(rev, seqno+1, fp)
644
646
645 def diffstat(patchlines):
647 def diffstat(patchlines):
646 if not util.find_in_path('diffstat', os.environ.get('PATH', '')):
648 if not util.find_exe('diffstat'):
647 return
649 return
648 fd, name = tempfile.mkstemp(prefix="hg-patchbomb-", suffix=".txt")
650 fd, name = tempfile.mkstemp(prefix="hg-patchbomb-", suffix=".txt")
649 try:
651 try:
650 p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name)
652 p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name)
651 try:
653 try:
652 for line in patchlines: print >> p.tochild, line
654 for line in patchlines: print >> p.tochild, line
653 p.tochild.close()
655 p.tochild.close()
654 if p.wait(): return
656 if p.wait(): return
655 fp = os.fdopen(fd, 'r')
657 fp = os.fdopen(fd, 'r')
656 stat = []
658 stat = []
657 for line in fp: stat.append(line.lstrip())
659 for line in fp: stat.append(line.lstrip())
658 last = stat.pop()
660 last = stat.pop()
659 stat.insert(0, last)
661 stat.insert(0, last)
660 stat = ''.join(stat)
662 stat = ''.join(stat)
661 if stat.startswith('0 files'): raise ValueError
663 if stat.startswith('0 files'): raise ValueError
662 return stat
664 return stat
663 except: raise
665 except: raise
664 finally:
666 finally:
665 try: os.unlink(name)
667 try: os.unlink(name)
666 except: pass
668 except: pass
@@ -1,1531 +1,1543 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 binary(s):
214 def binary(s):
215 """return true if a string is binary data using diff's heuristic"""
215 """return true if a string is binary data using diff's heuristic"""
216 if s and '\0' in s[:4096]:
216 if s and '\0' in s[:4096]:
217 return True
217 return True
218 return False
218 return False
219
219
220 def unique(g):
220 def unique(g):
221 """return the uniq elements of iterable g"""
221 """return the uniq elements of iterable g"""
222 seen = {}
222 seen = {}
223 l = []
223 l = []
224 for f in g:
224 for f in g:
225 if f not in seen:
225 if f not in seen:
226 seen[f] = 1
226 seen[f] = 1
227 l.append(f)
227 l.append(f)
228 return l
228 return l
229
229
230 class Abort(Exception):
230 class Abort(Exception):
231 """Raised if a command needs to print an error and exit."""
231 """Raised if a command needs to print an error and exit."""
232
232
233 class UnexpectedOutput(Abort):
233 class UnexpectedOutput(Abort):
234 """Raised to print an error with part of output and exit."""
234 """Raised to print an error with part of output and exit."""
235
235
236 def always(fn): return True
236 def always(fn): return True
237 def never(fn): return False
237 def never(fn): return False
238
238
239 def expand_glob(pats):
239 def expand_glob(pats):
240 '''On Windows, expand the implicit globs in a list of patterns'''
240 '''On Windows, expand the implicit globs in a list of patterns'''
241 if os.name != 'nt':
241 if os.name != 'nt':
242 return list(pats)
242 return list(pats)
243 ret = []
243 ret = []
244 for p in pats:
244 for p in pats:
245 kind, name = patkind(p, None)
245 kind, name = patkind(p, None)
246 if kind is None:
246 if kind is None:
247 globbed = glob.glob(name)
247 globbed = glob.glob(name)
248 if globbed:
248 if globbed:
249 ret.extend(globbed)
249 ret.extend(globbed)
250 continue
250 continue
251 # if we couldn't expand the glob, just keep it around
251 # if we couldn't expand the glob, just keep it around
252 ret.append(p)
252 ret.append(p)
253 return ret
253 return ret
254
254
255 def patkind(name, dflt_pat='glob'):
255 def patkind(name, dflt_pat='glob'):
256 """Split a string into an optional pattern kind prefix and the
256 """Split a string into an optional pattern kind prefix and the
257 actual pattern."""
257 actual pattern."""
258 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
258 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
259 if name.startswith(prefix + ':'): return name.split(':', 1)
259 if name.startswith(prefix + ':'): return name.split(':', 1)
260 return dflt_pat, name
260 return dflt_pat, name
261
261
262 def globre(pat, head='^', tail='$'):
262 def globre(pat, head='^', tail='$'):
263 "convert a glob pattern into a regexp"
263 "convert a glob pattern into a regexp"
264 i, n = 0, len(pat)
264 i, n = 0, len(pat)
265 res = ''
265 res = ''
266 group = False
266 group = False
267 def peek(): return i < n and pat[i]
267 def peek(): return i < n and pat[i]
268 while i < n:
268 while i < n:
269 c = pat[i]
269 c = pat[i]
270 i = i+1
270 i = i+1
271 if c == '*':
271 if c == '*':
272 if peek() == '*':
272 if peek() == '*':
273 i += 1
273 i += 1
274 res += '.*'
274 res += '.*'
275 else:
275 else:
276 res += '[^/]*'
276 res += '[^/]*'
277 elif c == '?':
277 elif c == '?':
278 res += '.'
278 res += '.'
279 elif c == '[':
279 elif c == '[':
280 j = i
280 j = i
281 if j < n and pat[j] in '!]':
281 if j < n and pat[j] in '!]':
282 j += 1
282 j += 1
283 while j < n and pat[j] != ']':
283 while j < n and pat[j] != ']':
284 j += 1
284 j += 1
285 if j >= n:
285 if j >= n:
286 res += '\\['
286 res += '\\['
287 else:
287 else:
288 stuff = pat[i:j].replace('\\','\\\\')
288 stuff = pat[i:j].replace('\\','\\\\')
289 i = j + 1
289 i = j + 1
290 if stuff[0] == '!':
290 if stuff[0] == '!':
291 stuff = '^' + stuff[1:]
291 stuff = '^' + stuff[1:]
292 elif stuff[0] == '^':
292 elif stuff[0] == '^':
293 stuff = '\\' + stuff
293 stuff = '\\' + stuff
294 res = '%s[%s]' % (res, stuff)
294 res = '%s[%s]' % (res, stuff)
295 elif c == '{':
295 elif c == '{':
296 group = True
296 group = True
297 res += '(?:'
297 res += '(?:'
298 elif c == '}' and group:
298 elif c == '}' and group:
299 res += ')'
299 res += ')'
300 group = False
300 group = False
301 elif c == ',' and group:
301 elif c == ',' and group:
302 res += '|'
302 res += '|'
303 elif c == '\\':
303 elif c == '\\':
304 p = peek()
304 p = peek()
305 if p:
305 if p:
306 i += 1
306 i += 1
307 res += re.escape(p)
307 res += re.escape(p)
308 else:
308 else:
309 res += re.escape(c)
309 res += re.escape(c)
310 else:
310 else:
311 res += re.escape(c)
311 res += re.escape(c)
312 return head + res + tail
312 return head + res + tail
313
313
314 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
314 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
315
315
316 def pathto(root, n1, n2):
316 def pathto(root, n1, n2):
317 '''return the relative path from one place to another.
317 '''return the relative path from one place to another.
318 root should use os.sep to separate directories
318 root should use os.sep to separate directories
319 n1 should use os.sep to separate directories
319 n1 should use os.sep to separate directories
320 n2 should use "/" to separate directories
320 n2 should use "/" to separate directories
321 returns an os.sep-separated path.
321 returns an os.sep-separated path.
322
322
323 If n1 is a relative path, it's assumed it's
323 If n1 is a relative path, it's assumed it's
324 relative to root.
324 relative to root.
325 n2 should always be relative to root.
325 n2 should always be relative to root.
326 '''
326 '''
327 if not n1: return localpath(n2)
327 if not n1: return localpath(n2)
328 if os.path.isabs(n1):
328 if os.path.isabs(n1):
329 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
329 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
330 return os.path.join(root, localpath(n2))
330 return os.path.join(root, localpath(n2))
331 n2 = '/'.join((pconvert(root), n2))
331 n2 = '/'.join((pconvert(root), n2))
332 a, b = n1.split(os.sep), n2.split('/')
332 a, b = n1.split(os.sep), n2.split('/')
333 a.reverse()
333 a.reverse()
334 b.reverse()
334 b.reverse()
335 while a and b and a[-1] == b[-1]:
335 while a and b and a[-1] == b[-1]:
336 a.pop()
336 a.pop()
337 b.pop()
337 b.pop()
338 b.reverse()
338 b.reverse()
339 return os.sep.join((['..'] * len(a)) + b)
339 return os.sep.join((['..'] * len(a)) + b)
340
340
341 def canonpath(root, cwd, myname):
341 def canonpath(root, cwd, myname):
342 """return the canonical path of myname, given cwd and root"""
342 """return the canonical path of myname, given cwd and root"""
343 if root == os.sep:
343 if root == os.sep:
344 rootsep = os.sep
344 rootsep = os.sep
345 elif root.endswith(os.sep):
345 elif root.endswith(os.sep):
346 rootsep = root
346 rootsep = root
347 else:
347 else:
348 rootsep = root + os.sep
348 rootsep = root + os.sep
349 name = myname
349 name = myname
350 if not os.path.isabs(name):
350 if not os.path.isabs(name):
351 name = os.path.join(root, cwd, name)
351 name = os.path.join(root, cwd, name)
352 name = os.path.normpath(name)
352 name = os.path.normpath(name)
353 if name != rootsep and name.startswith(rootsep):
353 if name != rootsep and name.startswith(rootsep):
354 name = name[len(rootsep):]
354 name = name[len(rootsep):]
355 audit_path(name)
355 audit_path(name)
356 return pconvert(name)
356 return pconvert(name)
357 elif name == root:
357 elif name == root:
358 return ''
358 return ''
359 else:
359 else:
360 # Determine whether `name' is in the hierarchy at or beneath `root',
360 # Determine whether `name' is in the hierarchy at or beneath `root',
361 # by iterating name=dirname(name) until that causes no change (can't
361 # by iterating name=dirname(name) until that causes no change (can't
362 # check name == '/', because that doesn't work on windows). For each
362 # check name == '/', because that doesn't work on windows). For each
363 # `name', compare dev/inode numbers. If they match, the list `rel'
363 # `name', compare dev/inode numbers. If they match, the list `rel'
364 # holds the reversed list of components making up the relative file
364 # holds the reversed list of components making up the relative file
365 # name we want.
365 # name we want.
366 root_st = os.stat(root)
366 root_st = os.stat(root)
367 rel = []
367 rel = []
368 while True:
368 while True:
369 try:
369 try:
370 name_st = os.stat(name)
370 name_st = os.stat(name)
371 except OSError:
371 except OSError:
372 break
372 break
373 if samestat(name_st, root_st):
373 if samestat(name_st, root_st):
374 if not rel:
374 if not rel:
375 # name was actually the same as root (maybe a symlink)
375 # name was actually the same as root (maybe a symlink)
376 return ''
376 return ''
377 rel.reverse()
377 rel.reverse()
378 name = os.path.join(*rel)
378 name = os.path.join(*rel)
379 audit_path(name)
379 audit_path(name)
380 return pconvert(name)
380 return pconvert(name)
381 dirname, basename = os.path.split(name)
381 dirname, basename = os.path.split(name)
382 rel.append(basename)
382 rel.append(basename)
383 if dirname == name:
383 if dirname == name:
384 break
384 break
385 name = dirname
385 name = dirname
386
386
387 raise Abort('%s not under root' % myname)
387 raise Abort('%s not under root' % myname)
388
388
389 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None):
389 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None):
390 return _matcher(canonroot, cwd, names, inc, exc, 'glob', src)
390 return _matcher(canonroot, cwd, names, inc, exc, 'glob', src)
391
391
392 def cmdmatcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None,
392 def cmdmatcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None,
393 globbed=False, default=None):
393 globbed=False, default=None):
394 default = default or 'relpath'
394 default = default or 'relpath'
395 if default == 'relpath' and not globbed:
395 if default == 'relpath' and not globbed:
396 names = expand_glob(names)
396 names = expand_glob(names)
397 return _matcher(canonroot, cwd, names, inc, exc, default, src)
397 return _matcher(canonroot, cwd, names, inc, exc, default, src)
398
398
399 def _matcher(canonroot, cwd, names, inc, exc, dflt_pat, src):
399 def _matcher(canonroot, cwd, names, inc, exc, dflt_pat, src):
400 """build a function to match a set of file patterns
400 """build a function to match a set of file patterns
401
401
402 arguments:
402 arguments:
403 canonroot - the canonical root of the tree you're matching against
403 canonroot - the canonical root of the tree you're matching against
404 cwd - the current working directory, if relevant
404 cwd - the current working directory, if relevant
405 names - patterns to find
405 names - patterns to find
406 inc - patterns to include
406 inc - patterns to include
407 exc - patterns to exclude
407 exc - patterns to exclude
408 dflt_pat - if a pattern in names has no explicit type, assume this one
408 dflt_pat - if a pattern in names has no explicit type, assume this one
409 src - where these patterns came from (e.g. .hgignore)
409 src - where these patterns came from (e.g. .hgignore)
410
410
411 a pattern is one of:
411 a pattern is one of:
412 'glob:<glob>' - a glob relative to cwd
412 'glob:<glob>' - a glob relative to cwd
413 're:<regexp>' - a regular expression
413 're:<regexp>' - a regular expression
414 'path:<path>' - a path relative to canonroot
414 'path:<path>' - a path relative to canonroot
415 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
415 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
416 'relpath:<path>' - a path relative to cwd
416 'relpath:<path>' - a path relative to cwd
417 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
417 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
418 '<something>' - one of the cases above, selected by the dflt_pat argument
418 '<something>' - one of the cases above, selected by the dflt_pat argument
419
419
420 returns:
420 returns:
421 a 3-tuple containing
421 a 3-tuple containing
422 - list of roots (places where one should start a recursive walk of the fs);
422 - list of roots (places where one should start a recursive walk of the fs);
423 this often matches the explicit non-pattern names passed in, but also
423 this often matches the explicit non-pattern names passed in, but also
424 includes the initial part of glob: patterns that has no glob characters
424 includes the initial part of glob: patterns that has no glob characters
425 - a bool match(filename) function
425 - a bool match(filename) function
426 - a bool indicating if any patterns were passed in
426 - a bool indicating if any patterns were passed in
427 """
427 """
428
428
429 # a common case: no patterns at all
429 # a common case: no patterns at all
430 if not names and not inc and not exc:
430 if not names and not inc and not exc:
431 return [], always, False
431 return [], always, False
432
432
433 def contains_glob(name):
433 def contains_glob(name):
434 for c in name:
434 for c in name:
435 if c in _globchars: return True
435 if c in _globchars: return True
436 return False
436 return False
437
437
438 def regex(kind, name, tail):
438 def regex(kind, name, tail):
439 '''convert a pattern into a regular expression'''
439 '''convert a pattern into a regular expression'''
440 if not name:
440 if not name:
441 return ''
441 return ''
442 if kind == 're':
442 if kind == 're':
443 return name
443 return name
444 elif kind == 'path':
444 elif kind == 'path':
445 return '^' + re.escape(name) + '(?:/|$)'
445 return '^' + re.escape(name) + '(?:/|$)'
446 elif kind == 'relglob':
446 elif kind == 'relglob':
447 return globre(name, '(?:|.*/)', tail)
447 return globre(name, '(?:|.*/)', tail)
448 elif kind == 'relpath':
448 elif kind == 'relpath':
449 return re.escape(name) + '(?:/|$)'
449 return re.escape(name) + '(?:/|$)'
450 elif kind == 'relre':
450 elif kind == 'relre':
451 if name.startswith('^'):
451 if name.startswith('^'):
452 return name
452 return name
453 return '.*' + name
453 return '.*' + name
454 return globre(name, '', tail)
454 return globre(name, '', tail)
455
455
456 def matchfn(pats, tail):
456 def matchfn(pats, tail):
457 """build a matching function from a set of patterns"""
457 """build a matching function from a set of patterns"""
458 if not pats:
458 if not pats:
459 return
459 return
460 try:
460 try:
461 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
461 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
462 return re.compile(pat).match
462 return re.compile(pat).match
463 except re.error:
463 except re.error:
464 for k, p in pats:
464 for k, p in pats:
465 try:
465 try:
466 re.compile('(?:%s)' % regex(k, p, tail))
466 re.compile('(?:%s)' % regex(k, p, tail))
467 except re.error:
467 except re.error:
468 if src:
468 if src:
469 raise Abort("%s: invalid pattern (%s): %s" %
469 raise Abort("%s: invalid pattern (%s): %s" %
470 (src, k, p))
470 (src, k, p))
471 else:
471 else:
472 raise Abort("invalid pattern (%s): %s" % (k, p))
472 raise Abort("invalid pattern (%s): %s" % (k, p))
473 raise Abort("invalid pattern")
473 raise Abort("invalid pattern")
474
474
475 def globprefix(pat):
475 def globprefix(pat):
476 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
476 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
477 root = []
477 root = []
478 for p in pat.split('/'):
478 for p in pat.split('/'):
479 if contains_glob(p): break
479 if contains_glob(p): break
480 root.append(p)
480 root.append(p)
481 return '/'.join(root) or '.'
481 return '/'.join(root) or '.'
482
482
483 def normalizepats(names, default):
483 def normalizepats(names, default):
484 pats = []
484 pats = []
485 roots = []
485 roots = []
486 anypats = False
486 anypats = False
487 for kind, name in [patkind(p, default) for p in names]:
487 for kind, name in [patkind(p, default) for p in names]:
488 if kind in ('glob', 'relpath'):
488 if kind in ('glob', 'relpath'):
489 name = canonpath(canonroot, cwd, name)
489 name = canonpath(canonroot, cwd, name)
490 elif kind in ('relglob', 'path'):
490 elif kind in ('relglob', 'path'):
491 name = normpath(name)
491 name = normpath(name)
492
492
493 pats.append((kind, name))
493 pats.append((kind, name))
494
494
495 if kind in ('glob', 're', 'relglob', 'relre'):
495 if kind in ('glob', 're', 'relglob', 'relre'):
496 anypats = True
496 anypats = True
497
497
498 if kind == 'glob':
498 if kind == 'glob':
499 root = globprefix(name)
499 root = globprefix(name)
500 roots.append(root)
500 roots.append(root)
501 elif kind in ('relpath', 'path'):
501 elif kind in ('relpath', 'path'):
502 roots.append(name or '.')
502 roots.append(name or '.')
503 elif kind == 'relglob':
503 elif kind == 'relglob':
504 roots.append('.')
504 roots.append('.')
505 return roots, pats, anypats
505 return roots, pats, anypats
506
506
507 roots, pats, anypats = normalizepats(names, dflt_pat)
507 roots, pats, anypats = normalizepats(names, dflt_pat)
508
508
509 patmatch = matchfn(pats, '$') or always
509 patmatch = matchfn(pats, '$') or always
510 incmatch = always
510 incmatch = always
511 if inc:
511 if inc:
512 dummy, inckinds, dummy = normalizepats(inc, 'glob')
512 dummy, inckinds, dummy = normalizepats(inc, 'glob')
513 incmatch = matchfn(inckinds, '(?:/|$)')
513 incmatch = matchfn(inckinds, '(?:/|$)')
514 excmatch = lambda fn: False
514 excmatch = lambda fn: False
515 if exc:
515 if exc:
516 dummy, exckinds, dummy = normalizepats(exc, 'glob')
516 dummy, exckinds, dummy = normalizepats(exc, 'glob')
517 excmatch = matchfn(exckinds, '(?:/|$)')
517 excmatch = matchfn(exckinds, '(?:/|$)')
518
518
519 if not names and inc and not exc:
519 if not names and inc and not exc:
520 # common case: hgignore patterns
520 # common case: hgignore patterns
521 match = incmatch
521 match = incmatch
522 else:
522 else:
523 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
523 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
524
524
525 return (roots, match, (inc or exc or anypats) and True)
525 return (roots, match, (inc or exc or anypats) and True)
526
526
527 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
527 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None):
528 '''enhanced shell command execution.
528 '''enhanced shell command execution.
529 run with environment maybe modified, maybe in different dir.
529 run with environment maybe modified, maybe in different dir.
530
530
531 if command fails and onerr is None, return status. if ui object,
531 if command fails and onerr is None, return status. if ui object,
532 print error message and return status, else raise onerr object as
532 print error message and return status, else raise onerr object as
533 exception.'''
533 exception.'''
534 def py2shell(val):
534 def py2shell(val):
535 'convert python object into string that is useful to shell'
535 'convert python object into string that is useful to shell'
536 if val in (None, False):
536 if val in (None, False):
537 return '0'
537 return '0'
538 if val == True:
538 if val == True:
539 return '1'
539 return '1'
540 return str(val)
540 return str(val)
541 oldenv = {}
541 oldenv = {}
542 for k in environ:
542 for k in environ:
543 oldenv[k] = os.environ.get(k)
543 oldenv[k] = os.environ.get(k)
544 if cwd is not None:
544 if cwd is not None:
545 oldcwd = os.getcwd()
545 oldcwd = os.getcwd()
546 origcmd = cmd
546 origcmd = cmd
547 if os.name == 'nt':
547 if os.name == 'nt':
548 cmd = '"%s"' % cmd
548 cmd = '"%s"' % cmd
549 try:
549 try:
550 for k, v in environ.iteritems():
550 for k, v in environ.iteritems():
551 os.environ[k] = py2shell(v)
551 os.environ[k] = py2shell(v)
552 if cwd is not None and oldcwd != cwd:
552 if cwd is not None and oldcwd != cwd:
553 os.chdir(cwd)
553 os.chdir(cwd)
554 rc = os.system(cmd)
554 rc = os.system(cmd)
555 if rc and onerr:
555 if rc and onerr:
556 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
556 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
557 explain_exit(rc)[0])
557 explain_exit(rc)[0])
558 if errprefix:
558 if errprefix:
559 errmsg = '%s: %s' % (errprefix, errmsg)
559 errmsg = '%s: %s' % (errprefix, errmsg)
560 try:
560 try:
561 onerr.warn(errmsg + '\n')
561 onerr.warn(errmsg + '\n')
562 except AttributeError:
562 except AttributeError:
563 raise onerr(errmsg)
563 raise onerr(errmsg)
564 return rc
564 return rc
565 finally:
565 finally:
566 for k, v in oldenv.iteritems():
566 for k, v in oldenv.iteritems():
567 if v is None:
567 if v is None:
568 del os.environ[k]
568 del os.environ[k]
569 else:
569 else:
570 os.environ[k] = v
570 os.environ[k] = v
571 if cwd is not None and oldcwd != cwd:
571 if cwd is not None and oldcwd != cwd:
572 os.chdir(oldcwd)
572 os.chdir(oldcwd)
573
573
574 # os.path.lexists is not available on python2.3
574 # os.path.lexists is not available on python2.3
575 def lexists(filename):
575 def lexists(filename):
576 "test whether a file with this name exists. does not follow symlinks"
576 "test whether a file with this name exists. does not follow symlinks"
577 try:
577 try:
578 os.lstat(filename)
578 os.lstat(filename)
579 except:
579 except:
580 return False
580 return False
581 return True
581 return True
582
582
583 def rename(src, dst):
583 def rename(src, dst):
584 """forcibly rename a file"""
584 """forcibly rename a file"""
585 try:
585 try:
586 os.rename(src, dst)
586 os.rename(src, dst)
587 except OSError, err:
587 except OSError, err:
588 # on windows, rename to existing file is not allowed, so we
588 # on windows, rename to existing file is not allowed, so we
589 # must delete destination first. but if file is open, unlink
589 # must delete destination first. but if file is open, unlink
590 # schedules it for delete but does not delete it. rename
590 # schedules it for delete but does not delete it. rename
591 # happens immediately even for open files, so we create
591 # happens immediately even for open files, so we create
592 # temporary file, delete it, rename destination to that name,
592 # temporary file, delete it, rename destination to that name,
593 # then delete that. then rename is safe to do.
593 # then delete that. then rename is safe to do.
594 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
594 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
595 os.close(fd)
595 os.close(fd)
596 os.unlink(temp)
596 os.unlink(temp)
597 os.rename(dst, temp)
597 os.rename(dst, temp)
598 os.unlink(temp)
598 os.unlink(temp)
599 os.rename(src, dst)
599 os.rename(src, dst)
600
600
601 def unlink(f):
601 def unlink(f):
602 """unlink and remove the directory if it is empty"""
602 """unlink and remove the directory if it is empty"""
603 os.unlink(f)
603 os.unlink(f)
604 # try removing directories that might now be empty
604 # try removing directories that might now be empty
605 try:
605 try:
606 os.removedirs(os.path.dirname(f))
606 os.removedirs(os.path.dirname(f))
607 except OSError:
607 except OSError:
608 pass
608 pass
609
609
610 def copyfile(src, dest):
610 def copyfile(src, dest):
611 "copy a file, preserving mode"
611 "copy a file, preserving mode"
612 if os.path.islink(src):
612 if os.path.islink(src):
613 try:
613 try:
614 os.unlink(dest)
614 os.unlink(dest)
615 except:
615 except:
616 pass
616 pass
617 os.symlink(os.readlink(src), dest)
617 os.symlink(os.readlink(src), dest)
618 else:
618 else:
619 try:
619 try:
620 shutil.copyfile(src, dest)
620 shutil.copyfile(src, dest)
621 shutil.copymode(src, dest)
621 shutil.copymode(src, dest)
622 except shutil.Error, inst:
622 except shutil.Error, inst:
623 raise Abort(str(inst))
623 raise Abort(str(inst))
624
624
625 def copyfiles(src, dst, hardlink=None):
625 def copyfiles(src, dst, hardlink=None):
626 """Copy a directory tree using hardlinks if possible"""
626 """Copy a directory tree using hardlinks if possible"""
627
627
628 if hardlink is None:
628 if hardlink is None:
629 hardlink = (os.stat(src).st_dev ==
629 hardlink = (os.stat(src).st_dev ==
630 os.stat(os.path.dirname(dst)).st_dev)
630 os.stat(os.path.dirname(dst)).st_dev)
631
631
632 if os.path.isdir(src):
632 if os.path.isdir(src):
633 os.mkdir(dst)
633 os.mkdir(dst)
634 for name in os.listdir(src):
634 for name in os.listdir(src):
635 srcname = os.path.join(src, name)
635 srcname = os.path.join(src, name)
636 dstname = os.path.join(dst, name)
636 dstname = os.path.join(dst, name)
637 copyfiles(srcname, dstname, hardlink)
637 copyfiles(srcname, dstname, hardlink)
638 else:
638 else:
639 if hardlink:
639 if hardlink:
640 try:
640 try:
641 os_link(src, dst)
641 os_link(src, dst)
642 except (IOError, OSError):
642 except (IOError, OSError):
643 hardlink = False
643 hardlink = False
644 shutil.copy(src, dst)
644 shutil.copy(src, dst)
645 else:
645 else:
646 shutil.copy(src, dst)
646 shutil.copy(src, dst)
647
647
648 def audit_path(path):
648 def audit_path(path):
649 """Abort if path contains dangerous components"""
649 """Abort if path contains dangerous components"""
650 parts = os.path.normcase(path).split(os.sep)
650 parts = os.path.normcase(path).split(os.sep)
651 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
651 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
652 or os.pardir in parts):
652 or os.pardir in parts):
653 raise Abort(_("path contains illegal component: %s\n") % path)
653 raise Abort(_("path contains illegal component: %s\n") % path)
654
654
655 def _makelock_file(info, pathname):
655 def _makelock_file(info, pathname):
656 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
656 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
657 os.write(ld, info)
657 os.write(ld, info)
658 os.close(ld)
658 os.close(ld)
659
659
660 def _readlock_file(pathname):
660 def _readlock_file(pathname):
661 return posixfile(pathname).read()
661 return posixfile(pathname).read()
662
662
663 def nlinks(pathname):
663 def nlinks(pathname):
664 """Return number of hardlinks for the given file."""
664 """Return number of hardlinks for the given file."""
665 return os.lstat(pathname).st_nlink
665 return os.lstat(pathname).st_nlink
666
666
667 if hasattr(os, 'link'):
667 if hasattr(os, 'link'):
668 os_link = os.link
668 os_link = os.link
669 else:
669 else:
670 def os_link(src, dst):
670 def os_link(src, dst):
671 raise OSError(0, _("Hardlinks not supported"))
671 raise OSError(0, _("Hardlinks not supported"))
672
672
673 def fstat(fp):
673 def fstat(fp):
674 '''stat file object that may not have fileno method.'''
674 '''stat file object that may not have fileno method.'''
675 try:
675 try:
676 return os.fstat(fp.fileno())
676 return os.fstat(fp.fileno())
677 except AttributeError:
677 except AttributeError:
678 return os.stat(fp.name)
678 return os.stat(fp.name)
679
679
680 posixfile = file
680 posixfile = file
681
681
682 def is_win_9x():
682 def is_win_9x():
683 '''return true if run on windows 95, 98 or me.'''
683 '''return true if run on windows 95, 98 or me.'''
684 try:
684 try:
685 return sys.getwindowsversion()[3] == 1
685 return sys.getwindowsversion()[3] == 1
686 except AttributeError:
686 except AttributeError:
687 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
687 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
688
688
689 getuser_fallback = None
689 getuser_fallback = None
690
690
691 def getuser():
691 def getuser():
692 '''return name of current user'''
692 '''return name of current user'''
693 try:
693 try:
694 return getpass.getuser()
694 return getpass.getuser()
695 except ImportError:
695 except ImportError:
696 # import of pwd will fail on windows - try fallback
696 # import of pwd will fail on windows - try fallback
697 if getuser_fallback:
697 if getuser_fallback:
698 return getuser_fallback()
698 return getuser_fallback()
699 # raised if win32api not available
699 # raised if win32api not available
700 raise Abort(_('user name not available - set USERNAME '
700 raise Abort(_('user name not available - set USERNAME '
701 'environment variable'))
701 'environment variable'))
702
702
703 def username(uid=None):
703 def username(uid=None):
704 """Return the name of the user with the given uid.
704 """Return the name of the user with the given uid.
705
705
706 If uid is None, return the name of the current user."""
706 If uid is None, return the name of the current user."""
707 try:
707 try:
708 import pwd
708 import pwd
709 if uid is None:
709 if uid is None:
710 uid = os.getuid()
710 uid = os.getuid()
711 try:
711 try:
712 return pwd.getpwuid(uid)[0]
712 return pwd.getpwuid(uid)[0]
713 except KeyError:
713 except KeyError:
714 return str(uid)
714 return str(uid)
715 except ImportError:
715 except ImportError:
716 return None
716 return None
717
717
718 def groupname(gid=None):
718 def groupname(gid=None):
719 """Return the name of the group with the given gid.
719 """Return the name of the group with the given gid.
720
720
721 If gid is None, return the name of the current group."""
721 If gid is None, return the name of the current group."""
722 try:
722 try:
723 import grp
723 import grp
724 if gid is None:
724 if gid is None:
725 gid = os.getgid()
725 gid = os.getgid()
726 try:
726 try:
727 return grp.getgrgid(gid)[0]
727 return grp.getgrgid(gid)[0]
728 except KeyError:
728 except KeyError:
729 return str(gid)
729 return str(gid)
730 except ImportError:
730 except ImportError:
731 return None
731 return None
732
732
733 # File system features
733 # File system features
734
734
735 def checkfolding(path):
735 def checkfolding(path):
736 """
736 """
737 Check whether the given path is on a case-sensitive filesystem
737 Check whether the given path is on a case-sensitive filesystem
738
738
739 Requires a path (like /foo/.hg) ending with a foldable final
739 Requires a path (like /foo/.hg) ending with a foldable final
740 directory component.
740 directory component.
741 """
741 """
742 s1 = os.stat(path)
742 s1 = os.stat(path)
743 d, b = os.path.split(path)
743 d, b = os.path.split(path)
744 p2 = os.path.join(d, b.upper())
744 p2 = os.path.join(d, b.upper())
745 if path == p2:
745 if path == p2:
746 p2 = os.path.join(d, b.lower())
746 p2 = os.path.join(d, b.lower())
747 try:
747 try:
748 s2 = os.stat(p2)
748 s2 = os.stat(p2)
749 if s2 == s1:
749 if s2 == s1:
750 return False
750 return False
751 return True
751 return True
752 except:
752 except:
753 return True
753 return True
754
754
755 def checkexec(path):
755 def checkexec(path):
756 """
756 """
757 Check whether the given path is on a filesystem with UNIX-like exec flags
757 Check whether the given path is on a filesystem with UNIX-like exec flags
758
758
759 Requires a directory (like /foo/.hg)
759 Requires a directory (like /foo/.hg)
760 """
760 """
761 fh, fn = tempfile.mkstemp("", "", path)
761 fh, fn = tempfile.mkstemp("", "", path)
762 os.close(fh)
762 os.close(fh)
763 m = os.stat(fn).st_mode
763 m = os.stat(fn).st_mode
764 os.chmod(fn, m ^ 0111)
764 os.chmod(fn, m ^ 0111)
765 r = (os.stat(fn).st_mode != m)
765 r = (os.stat(fn).st_mode != m)
766 os.unlink(fn)
766 os.unlink(fn)
767 return r
767 return r
768
768
769 def execfunc(path, fallback):
769 def execfunc(path, fallback):
770 '''return an is_exec() function with default to fallback'''
770 '''return an is_exec() function with default to fallback'''
771 if checkexec(path):
771 if checkexec(path):
772 return lambda x: is_exec(os.path.join(path, x))
772 return lambda x: is_exec(os.path.join(path, x))
773 return fallback
773 return fallback
774
774
775 def checklink(path):
775 def checklink(path):
776 """check whether the given path is on a symlink-capable filesystem"""
776 """check whether the given path is on a symlink-capable filesystem"""
777 # mktemp is not racy because symlink creation will fail if the
777 # mktemp is not racy because symlink creation will fail if the
778 # file already exists
778 # file already exists
779 name = tempfile.mktemp(dir=path)
779 name = tempfile.mktemp(dir=path)
780 try:
780 try:
781 os.symlink(".", name)
781 os.symlink(".", name)
782 os.unlink(name)
782 os.unlink(name)
783 return True
783 return True
784 except (OSError, AttributeError):
784 except (OSError, AttributeError):
785 return False
785 return False
786
786
787 def linkfunc(path, fallback):
787 def linkfunc(path, fallback):
788 '''return an is_link() function with default to fallback'''
788 '''return an is_link() function with default to fallback'''
789 if checklink(path):
789 if checklink(path):
790 return lambda x: os.path.islink(os.path.join(path, x))
790 return lambda x: os.path.islink(os.path.join(path, x))
791 return fallback
791 return fallback
792
792
793 _umask = os.umask(0)
793 _umask = os.umask(0)
794 os.umask(_umask)
794 os.umask(_umask)
795
795
796 def needbinarypatch():
796 def needbinarypatch():
797 """return True if patches should be applied in binary mode by default."""
797 """return True if patches should be applied in binary mode by default."""
798 return os.name == 'nt'
798 return os.name == 'nt'
799
799
800 # Platform specific variants
800 # Platform specific variants
801 if os.name == 'nt':
801 if os.name == 'nt':
802 import msvcrt
802 import msvcrt
803 nulldev = 'NUL:'
803 nulldev = 'NUL:'
804
804
805 class winstdout:
805 class winstdout:
806 '''stdout on windows misbehaves if sent through a pipe'''
806 '''stdout on windows misbehaves if sent through a pipe'''
807
807
808 def __init__(self, fp):
808 def __init__(self, fp):
809 self.fp = fp
809 self.fp = fp
810
810
811 def __getattr__(self, key):
811 def __getattr__(self, key):
812 return getattr(self.fp, key)
812 return getattr(self.fp, key)
813
813
814 def close(self):
814 def close(self):
815 try:
815 try:
816 self.fp.close()
816 self.fp.close()
817 except: pass
817 except: pass
818
818
819 def write(self, s):
819 def write(self, s):
820 try:
820 try:
821 return self.fp.write(s)
821 return self.fp.write(s)
822 except IOError, inst:
822 except IOError, inst:
823 if inst.errno != 0: raise
823 if inst.errno != 0: raise
824 self.close()
824 self.close()
825 raise IOError(errno.EPIPE, 'Broken pipe')
825 raise IOError(errno.EPIPE, 'Broken pipe')
826
826
827 def flush(self):
827 def flush(self):
828 try:
828 try:
829 return self.fp.flush()
829 return self.fp.flush()
830 except IOError, inst:
830 except IOError, inst:
831 if inst.errno != errno.EINVAL: raise
831 if inst.errno != errno.EINVAL: raise
832 self.close()
832 self.close()
833 raise IOError(errno.EPIPE, 'Broken pipe')
833 raise IOError(errno.EPIPE, 'Broken pipe')
834
834
835 sys.stdout = winstdout(sys.stdout)
835 sys.stdout = winstdout(sys.stdout)
836
836
837 def system_rcpath():
837 def system_rcpath():
838 try:
838 try:
839 return system_rcpath_win32()
839 return system_rcpath_win32()
840 except:
840 except:
841 return [r'c:\mercurial\mercurial.ini']
841 return [r'c:\mercurial\mercurial.ini']
842
842
843 def user_rcpath():
843 def user_rcpath():
844 '''return os-specific hgrc search path to the user dir'''
844 '''return os-specific hgrc search path to the user dir'''
845 try:
845 try:
846 userrc = user_rcpath_win32()
846 userrc = user_rcpath_win32()
847 except:
847 except:
848 userrc = os.path.join(os.path.expanduser('~'), 'mercurial.ini')
848 userrc = os.path.join(os.path.expanduser('~'), 'mercurial.ini')
849 path = [userrc]
849 path = [userrc]
850 userprofile = os.environ.get('USERPROFILE')
850 userprofile = os.environ.get('USERPROFILE')
851 if userprofile:
851 if userprofile:
852 path.append(os.path.join(userprofile, 'mercurial.ini'))
852 path.append(os.path.join(userprofile, 'mercurial.ini'))
853 return path
853 return path
854
854
855 def parse_patch_output(output_line):
855 def parse_patch_output(output_line):
856 """parses the output produced by patch and returns the file name"""
856 """parses the output produced by patch and returns the file name"""
857 pf = output_line[14:]
857 pf = output_line[14:]
858 if pf[0] == '`':
858 if pf[0] == '`':
859 pf = pf[1:-1] # Remove the quotes
859 pf = pf[1:-1] # Remove the quotes
860 return pf
860 return pf
861
861
862 def testpid(pid):
862 def testpid(pid):
863 '''return False if pid dead, True if running or not known'''
863 '''return False if pid dead, True if running or not known'''
864 return True
864 return True
865
865
866 def set_exec(f, mode):
866 def set_exec(f, mode):
867 pass
867 pass
868
868
869 def set_link(f, mode):
869 def set_link(f, mode):
870 pass
870 pass
871
871
872 def set_binary(fd):
872 def set_binary(fd):
873 msvcrt.setmode(fd.fileno(), os.O_BINARY)
873 msvcrt.setmode(fd.fileno(), os.O_BINARY)
874
874
875 def pconvert(path):
875 def pconvert(path):
876 return path.replace("\\", "/")
876 return path.replace("\\", "/")
877
877
878 def localpath(path):
878 def localpath(path):
879 return path.replace('/', '\\')
879 return path.replace('/', '\\')
880
880
881 def normpath(path):
881 def normpath(path):
882 return pconvert(os.path.normpath(path))
882 return pconvert(os.path.normpath(path))
883
883
884 makelock = _makelock_file
884 makelock = _makelock_file
885 readlock = _readlock_file
885 readlock = _readlock_file
886
886
887 def samestat(s1, s2):
887 def samestat(s1, s2):
888 return False
888 return False
889
889
890 # A sequence of backslashes is special iff it precedes a double quote:
890 # A sequence of backslashes is special iff it precedes a double quote:
891 # - if there's an even number of backslashes, the double quote is not
891 # - if there's an even number of backslashes, the double quote is not
892 # quoted (i.e. it ends the quoted region)
892 # quoted (i.e. it ends the quoted region)
893 # - if there's an odd number of backslashes, the double quote is quoted
893 # - if there's an odd number of backslashes, the double quote is quoted
894 # - in both cases, every pair of backslashes is unquoted into a single
894 # - in both cases, every pair of backslashes is unquoted into a single
895 # backslash
895 # backslash
896 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
896 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
897 # So, to quote a string, we must surround it in double quotes, double
897 # So, to quote a string, we must surround it in double quotes, double
898 # the number of backslashes that preceed double quotes and add another
898 # the number of backslashes that preceed double quotes and add another
899 # backslash before every double quote (being careful with the double
899 # backslash before every double quote (being careful with the double
900 # quote we've appended to the end)
900 # quote we've appended to the end)
901 _quotere = None
901 _quotere = None
902 def shellquote(s):
902 def shellquote(s):
903 global _quotere
903 global _quotere
904 if _quotere is None:
904 if _quotere is None:
905 _quotere = re.compile(r'(\\*)("|\\$)')
905 _quotere = re.compile(r'(\\*)("|\\$)')
906 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
906 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
907
907
908 def explain_exit(code):
908 def explain_exit(code):
909 return _("exited with status %d") % code, code
909 return _("exited with status %d") % code, code
910
910
911 # if you change this stub into a real check, please try to implement the
911 # if you change this stub into a real check, please try to implement the
912 # username and groupname functions above, too.
912 # username and groupname functions above, too.
913 def isowner(fp, st=None):
913 def isowner(fp, st=None):
914 return True
914 return True
915
915
916 def find_in_path(name, path, default=None):
916 def find_in_path(name, path, default=None):
917 '''find name in search path. path can be string (will be split
917 '''find name in search path. path can be string (will be split
918 with os.pathsep), or iterable thing that returns strings. if name
918 with os.pathsep), or iterable thing that returns strings. if name
919 found, return path to name. else return default. name is looked up
919 found, return path to name. else return default. name is looked up
920 using cmd.exe rules, using PATHEXT.'''
920 using cmd.exe rules, using PATHEXT.'''
921 if isinstance(path, str):
921 if isinstance(path, str):
922 path = path.split(os.pathsep)
922 path = path.split(os.pathsep)
923
923
924 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
924 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
925 pathext = pathext.lower().split(os.pathsep)
925 pathext = pathext.lower().split(os.pathsep)
926 isexec = os.path.splitext(name)[1].lower() in pathext
926 isexec = os.path.splitext(name)[1].lower() in pathext
927
927
928 for p in path:
928 for p in path:
929 p_name = os.path.join(p, name)
929 p_name = os.path.join(p, name)
930
930
931 if isexec and os.path.exists(p_name):
931 if isexec and os.path.exists(p_name):
932 return p_name
932 return p_name
933
933
934 for ext in pathext:
934 for ext in pathext:
935 p_name_ext = p_name + ext
935 p_name_ext = p_name + ext
936 if os.path.exists(p_name_ext):
936 if os.path.exists(p_name_ext):
937 return p_name_ext
937 return p_name_ext
938 return default
938 return default
939
939
940 try:
940 try:
941 # override functions with win32 versions if possible
941 # override functions with win32 versions if possible
942 from util_win32 import *
942 from util_win32 import *
943 if not is_win_9x():
943 if not is_win_9x():
944 posixfile = posixfile_nt
944 posixfile = posixfile_nt
945 except ImportError:
945 except ImportError:
946 pass
946 pass
947
947
948 else:
948 else:
949 nulldev = '/dev/null'
949 nulldev = '/dev/null'
950
950
951 def rcfiles(path):
951 def rcfiles(path):
952 rcs = [os.path.join(path, 'hgrc')]
952 rcs = [os.path.join(path, 'hgrc')]
953 rcdir = os.path.join(path, 'hgrc.d')
953 rcdir = os.path.join(path, 'hgrc.d')
954 try:
954 try:
955 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
955 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
956 if f.endswith(".rc")])
956 if f.endswith(".rc")])
957 except OSError:
957 except OSError:
958 pass
958 pass
959 return rcs
959 return rcs
960
960
961 def system_rcpath():
961 def system_rcpath():
962 path = []
962 path = []
963 # old mod_python does not set sys.argv
963 # old mod_python does not set sys.argv
964 if len(getattr(sys, 'argv', [])) > 0:
964 if len(getattr(sys, 'argv', [])) > 0:
965 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
965 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
966 '/../etc/mercurial'))
966 '/../etc/mercurial'))
967 path.extend(rcfiles('/etc/mercurial'))
967 path.extend(rcfiles('/etc/mercurial'))
968 return path
968 return path
969
969
970 def user_rcpath():
970 def user_rcpath():
971 return [os.path.expanduser('~/.hgrc')]
971 return [os.path.expanduser('~/.hgrc')]
972
972
973 def parse_patch_output(output_line):
973 def parse_patch_output(output_line):
974 """parses the output produced by patch and returns the file name"""
974 """parses the output produced by patch and returns the file name"""
975 pf = output_line[14:]
975 pf = output_line[14:]
976 if pf.startswith("'") and pf.endswith("'") and " " in pf:
976 if pf.startswith("'") and pf.endswith("'") and " " in pf:
977 pf = pf[1:-1] # Remove the quotes
977 pf = pf[1:-1] # Remove the quotes
978 return pf
978 return pf
979
979
980 def is_exec(f):
980 def is_exec(f):
981 """check whether a file is executable"""
981 """check whether a file is executable"""
982 return (os.lstat(f).st_mode & 0100 != 0)
982 return (os.lstat(f).st_mode & 0100 != 0)
983
983
984 def set_exec(f, mode):
984 def set_exec(f, mode):
985 s = os.lstat(f).st_mode
985 s = os.lstat(f).st_mode
986 if (s & 0100 != 0) == mode:
986 if (s & 0100 != 0) == mode:
987 return
987 return
988 if mode:
988 if mode:
989 # Turn on +x for every +r bit when making a file executable
989 # Turn on +x for every +r bit when making a file executable
990 # and obey umask.
990 # and obey umask.
991 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
991 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
992 else:
992 else:
993 os.chmod(f, s & 0666)
993 os.chmod(f, s & 0666)
994
994
995 def set_link(f, mode):
995 def set_link(f, mode):
996 """make a file a symbolic link/regular file
996 """make a file a symbolic link/regular file
997
997
998 if a file is changed to a link, its contents become the link data
998 if a file is changed to a link, its contents become the link data
999 if a link is changed to a file, its link data become its contents
999 if a link is changed to a file, its link data become its contents
1000 """
1000 """
1001
1001
1002 m = os.path.islink(f)
1002 m = os.path.islink(f)
1003 if m == bool(mode):
1003 if m == bool(mode):
1004 return
1004 return
1005
1005
1006 if mode: # switch file to link
1006 if mode: # switch file to link
1007 data = file(f).read()
1007 data = file(f).read()
1008 os.unlink(f)
1008 os.unlink(f)
1009 os.symlink(data, f)
1009 os.symlink(data, f)
1010 else:
1010 else:
1011 data = os.readlink(f)
1011 data = os.readlink(f)
1012 os.unlink(f)
1012 os.unlink(f)
1013 file(f, "w").write(data)
1013 file(f, "w").write(data)
1014
1014
1015 def set_binary(fd):
1015 def set_binary(fd):
1016 pass
1016 pass
1017
1017
1018 def pconvert(path):
1018 def pconvert(path):
1019 return path
1019 return path
1020
1020
1021 def localpath(path):
1021 def localpath(path):
1022 return path
1022 return path
1023
1023
1024 normpath = os.path.normpath
1024 normpath = os.path.normpath
1025 samestat = os.path.samestat
1025 samestat = os.path.samestat
1026
1026
1027 def makelock(info, pathname):
1027 def makelock(info, pathname):
1028 try:
1028 try:
1029 os.symlink(info, pathname)
1029 os.symlink(info, pathname)
1030 except OSError, why:
1030 except OSError, why:
1031 if why.errno == errno.EEXIST:
1031 if why.errno == errno.EEXIST:
1032 raise
1032 raise
1033 else:
1033 else:
1034 _makelock_file(info, pathname)
1034 _makelock_file(info, pathname)
1035
1035
1036 def readlock(pathname):
1036 def readlock(pathname):
1037 try:
1037 try:
1038 return os.readlink(pathname)
1038 return os.readlink(pathname)
1039 except OSError, why:
1039 except OSError, why:
1040 if why.errno == errno.EINVAL:
1040 if why.errno == errno.EINVAL:
1041 return _readlock_file(pathname)
1041 return _readlock_file(pathname)
1042 else:
1042 else:
1043 raise
1043 raise
1044
1044
1045 def shellquote(s):
1045 def shellquote(s):
1046 return "'%s'" % s.replace("'", "'\\''")
1046 return "'%s'" % s.replace("'", "'\\''")
1047
1047
1048 def testpid(pid):
1048 def testpid(pid):
1049 '''return False if pid dead, True if running or not sure'''
1049 '''return False if pid dead, True if running or not sure'''
1050 try:
1050 try:
1051 os.kill(pid, 0)
1051 os.kill(pid, 0)
1052 return True
1052 return True
1053 except OSError, inst:
1053 except OSError, inst:
1054 return inst.errno != errno.ESRCH
1054 return inst.errno != errno.ESRCH
1055
1055
1056 def explain_exit(code):
1056 def explain_exit(code):
1057 """return a 2-tuple (desc, code) describing a process's status"""
1057 """return a 2-tuple (desc, code) describing a process's status"""
1058 if os.WIFEXITED(code):
1058 if os.WIFEXITED(code):
1059 val = os.WEXITSTATUS(code)
1059 val = os.WEXITSTATUS(code)
1060 return _("exited with status %d") % val, val
1060 return _("exited with status %d") % val, val
1061 elif os.WIFSIGNALED(code):
1061 elif os.WIFSIGNALED(code):
1062 val = os.WTERMSIG(code)
1062 val = os.WTERMSIG(code)
1063 return _("killed by signal %d") % val, val
1063 return _("killed by signal %d") % val, val
1064 elif os.WIFSTOPPED(code):
1064 elif os.WIFSTOPPED(code):
1065 val = os.WSTOPSIG(code)
1065 val = os.WSTOPSIG(code)
1066 return _("stopped by signal %d") % val, val
1066 return _("stopped by signal %d") % val, val
1067 raise ValueError(_("invalid exit code"))
1067 raise ValueError(_("invalid exit code"))
1068
1068
1069 def isowner(fp, st=None):
1069 def isowner(fp, st=None):
1070 """Return True if the file object f belongs to the current user.
1070 """Return True if the file object f belongs to the current user.
1071
1071
1072 The return value of a util.fstat(f) may be passed as the st argument.
1072 The return value of a util.fstat(f) may be passed as the st argument.
1073 """
1073 """
1074 if st is None:
1074 if st is None:
1075 st = fstat(fp)
1075 st = fstat(fp)
1076 return st.st_uid == os.getuid()
1076 return st.st_uid == os.getuid()
1077
1077
1078 def find_in_path(name, path, default=None):
1078 def find_in_path(name, path, default=None):
1079 '''find name in search path. path can be string (will be split
1079 '''find name in search path. path can be string (will be split
1080 with os.pathsep), or iterable thing that returns strings. if name
1080 with os.pathsep), or iterable thing that returns strings. if name
1081 found, return path to name. else return default.'''
1081 found, return path to name. else return default.'''
1082 if isinstance(path, str):
1082 if isinstance(path, str):
1083 path = path.split(os.pathsep)
1083 path = path.split(os.pathsep)
1084 for p in path:
1084 for p in path:
1085 p_name = os.path.join(p, name)
1085 p_name = os.path.join(p, name)
1086 if os.path.exists(p_name):
1086 if os.path.exists(p_name):
1087 return p_name
1087 return p_name
1088 return default
1088 return default
1089
1089
1090 def find_exe(name, default=None):
1091 '''find path of an executable.
1092 if name contains a path component, return it as is. otherwise,
1093 use normal executable search path.'''
1094
1095 if os.sep in name:
1096 # don't check the executable bit. if the file isn't
1097 # executable, whoever tries to actually run it will give a
1098 # much more useful error message.
1099 return name
1100 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1101
1090 def _buildencodefun():
1102 def _buildencodefun():
1091 e = '_'
1103 e = '_'
1092 win_reserved = [ord(x) for x in '\\:*?"<>|']
1104 win_reserved = [ord(x) for x in '\\:*?"<>|']
1093 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1105 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1094 for x in (range(32) + range(126, 256) + win_reserved):
1106 for x in (range(32) + range(126, 256) + win_reserved):
1095 cmap[chr(x)] = "~%02x" % x
1107 cmap[chr(x)] = "~%02x" % x
1096 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1108 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1097 cmap[chr(x)] = e + chr(x).lower()
1109 cmap[chr(x)] = e + chr(x).lower()
1098 dmap = {}
1110 dmap = {}
1099 for k, v in cmap.iteritems():
1111 for k, v in cmap.iteritems():
1100 dmap[v] = k
1112 dmap[v] = k
1101 def decode(s):
1113 def decode(s):
1102 i = 0
1114 i = 0
1103 while i < len(s):
1115 while i < len(s):
1104 for l in xrange(1, 4):
1116 for l in xrange(1, 4):
1105 try:
1117 try:
1106 yield dmap[s[i:i+l]]
1118 yield dmap[s[i:i+l]]
1107 i += l
1119 i += l
1108 break
1120 break
1109 except KeyError:
1121 except KeyError:
1110 pass
1122 pass
1111 else:
1123 else:
1112 raise KeyError
1124 raise KeyError
1113 return (lambda s: "".join([cmap[c] for c in s]),
1125 return (lambda s: "".join([cmap[c] for c in s]),
1114 lambda s: "".join(list(decode(s))))
1126 lambda s: "".join(list(decode(s))))
1115
1127
1116 encodefilename, decodefilename = _buildencodefun()
1128 encodefilename, decodefilename = _buildencodefun()
1117
1129
1118 def encodedopener(openerfn, fn):
1130 def encodedopener(openerfn, fn):
1119 def o(path, *args, **kw):
1131 def o(path, *args, **kw):
1120 return openerfn(fn(path), *args, **kw)
1132 return openerfn(fn(path), *args, **kw)
1121 return o
1133 return o
1122
1134
1123 def opener(base, audit=True):
1135 def opener(base, audit=True):
1124 """
1136 """
1125 return a function that opens files relative to base
1137 return a function that opens files relative to base
1126
1138
1127 this function is used to hide the details of COW semantics and
1139 this function is used to hide the details of COW semantics and
1128 remote file access from higher level code.
1140 remote file access from higher level code.
1129 """
1141 """
1130 p = base
1142 p = base
1131 audit_p = audit
1143 audit_p = audit
1132
1144
1133 def mktempcopy(name, emptyok=False):
1145 def mktempcopy(name, emptyok=False):
1134 d, fn = os.path.split(name)
1146 d, fn = os.path.split(name)
1135 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1147 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1136 os.close(fd)
1148 os.close(fd)
1137 # Temporary files are created with mode 0600, which is usually not
1149 # Temporary files are created with mode 0600, which is usually not
1138 # what we want. If the original file already exists, just copy
1150 # what we want. If the original file already exists, just copy
1139 # its mode. Otherwise, manually obey umask.
1151 # its mode. Otherwise, manually obey umask.
1140 try:
1152 try:
1141 st_mode = os.lstat(name).st_mode
1153 st_mode = os.lstat(name).st_mode
1142 except OSError, inst:
1154 except OSError, inst:
1143 if inst.errno != errno.ENOENT:
1155 if inst.errno != errno.ENOENT:
1144 raise
1156 raise
1145 st_mode = 0666 & ~_umask
1157 st_mode = 0666 & ~_umask
1146 os.chmod(temp, st_mode)
1158 os.chmod(temp, st_mode)
1147 if emptyok:
1159 if emptyok:
1148 return temp
1160 return temp
1149 try:
1161 try:
1150 try:
1162 try:
1151 ifp = posixfile(name, "rb")
1163 ifp = posixfile(name, "rb")
1152 except IOError, inst:
1164 except IOError, inst:
1153 if inst.errno == errno.ENOENT:
1165 if inst.errno == errno.ENOENT:
1154 return temp
1166 return temp
1155 if not getattr(inst, 'filename', None):
1167 if not getattr(inst, 'filename', None):
1156 inst.filename = name
1168 inst.filename = name
1157 raise
1169 raise
1158 ofp = posixfile(temp, "wb")
1170 ofp = posixfile(temp, "wb")
1159 for chunk in filechunkiter(ifp):
1171 for chunk in filechunkiter(ifp):
1160 ofp.write(chunk)
1172 ofp.write(chunk)
1161 ifp.close()
1173 ifp.close()
1162 ofp.close()
1174 ofp.close()
1163 except:
1175 except:
1164 try: os.unlink(temp)
1176 try: os.unlink(temp)
1165 except: pass
1177 except: pass
1166 raise
1178 raise
1167 return temp
1179 return temp
1168
1180
1169 class atomictempfile(posixfile):
1181 class atomictempfile(posixfile):
1170 """the file will only be copied when rename is called"""
1182 """the file will only be copied when rename is called"""
1171 def __init__(self, name, mode):
1183 def __init__(self, name, mode):
1172 self.__name = name
1184 self.__name = name
1173 self.temp = mktempcopy(name, emptyok=('w' in mode))
1185 self.temp = mktempcopy(name, emptyok=('w' in mode))
1174 posixfile.__init__(self, self.temp, mode)
1186 posixfile.__init__(self, self.temp, mode)
1175 def rename(self):
1187 def rename(self):
1176 if not self.closed:
1188 if not self.closed:
1177 posixfile.close(self)
1189 posixfile.close(self)
1178 rename(self.temp, localpath(self.__name))
1190 rename(self.temp, localpath(self.__name))
1179 def __del__(self):
1191 def __del__(self):
1180 if not self.closed:
1192 if not self.closed:
1181 try:
1193 try:
1182 os.unlink(self.temp)
1194 os.unlink(self.temp)
1183 except: pass
1195 except: pass
1184 posixfile.close(self)
1196 posixfile.close(self)
1185
1197
1186 class atomicfile(atomictempfile):
1198 class atomicfile(atomictempfile):
1187 """the file will only be copied on close"""
1199 """the file will only be copied on close"""
1188 def __init__(self, name, mode):
1200 def __init__(self, name, mode):
1189 self._err = False
1201 self._err = False
1190 atomictempfile.__init__(self, name, mode)
1202 atomictempfile.__init__(self, name, mode)
1191 def write(self, s):
1203 def write(self, s):
1192 try:
1204 try:
1193 atomictempfile.write(self, s)
1205 atomictempfile.write(self, s)
1194 except:
1206 except:
1195 self._err = True
1207 self._err = True
1196 raise
1208 raise
1197 def close(self):
1209 def close(self):
1198 self.rename()
1210 self.rename()
1199 def __del__(self):
1211 def __del__(self):
1200 if not self._err:
1212 if not self._err:
1201 self.rename()
1213 self.rename()
1202
1214
1203 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
1215 def o(path, mode="r", text=False, atomic=False, atomictemp=False):
1204 if audit_p:
1216 if audit_p:
1205 audit_path(path)
1217 audit_path(path)
1206 f = os.path.join(p, path)
1218 f = os.path.join(p, path)
1207
1219
1208 if not text:
1220 if not text:
1209 mode += "b" # for that other OS
1221 mode += "b" # for that other OS
1210
1222
1211 if mode[0] != "r":
1223 if mode[0] != "r":
1212 try:
1224 try:
1213 nlink = nlinks(f)
1225 nlink = nlinks(f)
1214 except OSError:
1226 except OSError:
1215 nlink = 0
1227 nlink = 0
1216 d = os.path.dirname(f)
1228 d = os.path.dirname(f)
1217 if not os.path.isdir(d):
1229 if not os.path.isdir(d):
1218 os.makedirs(d)
1230 os.makedirs(d)
1219 if atomic:
1231 if atomic:
1220 return atomicfile(f, mode)
1232 return atomicfile(f, mode)
1221 elif atomictemp:
1233 elif atomictemp:
1222 return atomictempfile(f, mode)
1234 return atomictempfile(f, mode)
1223 if nlink > 1:
1235 if nlink > 1:
1224 rename(mktempcopy(f), f)
1236 rename(mktempcopy(f), f)
1225 return posixfile(f, mode)
1237 return posixfile(f, mode)
1226
1238
1227 return o
1239 return o
1228
1240
1229 class chunkbuffer(object):
1241 class chunkbuffer(object):
1230 """Allow arbitrary sized chunks of data to be efficiently read from an
1242 """Allow arbitrary sized chunks of data to be efficiently read from an
1231 iterator over chunks of arbitrary size."""
1243 iterator over chunks of arbitrary size."""
1232
1244
1233 def __init__(self, in_iter, targetsize = 2**16):
1245 def __init__(self, in_iter, targetsize = 2**16):
1234 """in_iter is the iterator that's iterating over the input chunks.
1246 """in_iter is the iterator that's iterating over the input chunks.
1235 targetsize is how big a buffer to try to maintain."""
1247 targetsize is how big a buffer to try to maintain."""
1236 self.in_iter = iter(in_iter)
1248 self.in_iter = iter(in_iter)
1237 self.buf = ''
1249 self.buf = ''
1238 self.targetsize = int(targetsize)
1250 self.targetsize = int(targetsize)
1239 if self.targetsize <= 0:
1251 if self.targetsize <= 0:
1240 raise ValueError(_("targetsize must be greater than 0, was %d") %
1252 raise ValueError(_("targetsize must be greater than 0, was %d") %
1241 targetsize)
1253 targetsize)
1242 self.iterempty = False
1254 self.iterempty = False
1243
1255
1244 def fillbuf(self):
1256 def fillbuf(self):
1245 """Ignore target size; read every chunk from iterator until empty."""
1257 """Ignore target size; read every chunk from iterator until empty."""
1246 if not self.iterempty:
1258 if not self.iterempty:
1247 collector = cStringIO.StringIO()
1259 collector = cStringIO.StringIO()
1248 collector.write(self.buf)
1260 collector.write(self.buf)
1249 for ch in self.in_iter:
1261 for ch in self.in_iter:
1250 collector.write(ch)
1262 collector.write(ch)
1251 self.buf = collector.getvalue()
1263 self.buf = collector.getvalue()
1252 self.iterempty = True
1264 self.iterempty = True
1253
1265
1254 def read(self, l):
1266 def read(self, l):
1255 """Read L bytes of data from the iterator of chunks of data.
1267 """Read L bytes of data from the iterator of chunks of data.
1256 Returns less than L bytes if the iterator runs dry."""
1268 Returns less than L bytes if the iterator runs dry."""
1257 if l > len(self.buf) and not self.iterempty:
1269 if l > len(self.buf) and not self.iterempty:
1258 # Clamp to a multiple of self.targetsize
1270 # Clamp to a multiple of self.targetsize
1259 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1271 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1260 collector = cStringIO.StringIO()
1272 collector = cStringIO.StringIO()
1261 collector.write(self.buf)
1273 collector.write(self.buf)
1262 collected = len(self.buf)
1274 collected = len(self.buf)
1263 for chunk in self.in_iter:
1275 for chunk in self.in_iter:
1264 collector.write(chunk)
1276 collector.write(chunk)
1265 collected += len(chunk)
1277 collected += len(chunk)
1266 if collected >= targetsize:
1278 if collected >= targetsize:
1267 break
1279 break
1268 if collected < targetsize:
1280 if collected < targetsize:
1269 self.iterempty = True
1281 self.iterempty = True
1270 self.buf = collector.getvalue()
1282 self.buf = collector.getvalue()
1271 s, self.buf = self.buf[:l], buffer(self.buf, l)
1283 s, self.buf = self.buf[:l], buffer(self.buf, l)
1272 return s
1284 return s
1273
1285
1274 def filechunkiter(f, size=65536, limit=None):
1286 def filechunkiter(f, size=65536, limit=None):
1275 """Create a generator that produces the data in the file size
1287 """Create a generator that produces the data in the file size
1276 (default 65536) bytes at a time, up to optional limit (default is
1288 (default 65536) bytes at a time, up to optional limit (default is
1277 to read all data). Chunks may be less than size bytes if the
1289 to read all data). Chunks may be less than size bytes if the
1278 chunk is the last chunk in the file, or the file is a socket or
1290 chunk is the last chunk in the file, or the file is a socket or
1279 some other type of file that sometimes reads less data than is
1291 some other type of file that sometimes reads less data than is
1280 requested."""
1292 requested."""
1281 assert size >= 0
1293 assert size >= 0
1282 assert limit is None or limit >= 0
1294 assert limit is None or limit >= 0
1283 while True:
1295 while True:
1284 if limit is None: nbytes = size
1296 if limit is None: nbytes = size
1285 else: nbytes = min(limit, size)
1297 else: nbytes = min(limit, size)
1286 s = nbytes and f.read(nbytes)
1298 s = nbytes and f.read(nbytes)
1287 if not s: break
1299 if not s: break
1288 if limit: limit -= len(s)
1300 if limit: limit -= len(s)
1289 yield s
1301 yield s
1290
1302
1291 def makedate():
1303 def makedate():
1292 lt = time.localtime()
1304 lt = time.localtime()
1293 if lt[8] == 1 and time.daylight:
1305 if lt[8] == 1 and time.daylight:
1294 tz = time.altzone
1306 tz = time.altzone
1295 else:
1307 else:
1296 tz = time.timezone
1308 tz = time.timezone
1297 return time.mktime(lt), tz
1309 return time.mktime(lt), tz
1298
1310
1299 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1311 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1300 """represent a (unixtime, offset) tuple as a localized time.
1312 """represent a (unixtime, offset) tuple as a localized time.
1301 unixtime is seconds since the epoch, and offset is the time zone's
1313 unixtime is seconds since the epoch, and offset is the time zone's
1302 number of seconds away from UTC. if timezone is false, do not
1314 number of seconds away from UTC. if timezone is false, do not
1303 append time zone to string."""
1315 append time zone to string."""
1304 t, tz = date or makedate()
1316 t, tz = date or makedate()
1305 s = time.strftime(format, time.gmtime(float(t) - tz))
1317 s = time.strftime(format, time.gmtime(float(t) - tz))
1306 if timezone:
1318 if timezone:
1307 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1319 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1308 return s
1320 return s
1309
1321
1310 def strdate(string, format, defaults):
1322 def strdate(string, format, defaults):
1311 """parse a localized time string and return a (unixtime, offset) tuple.
1323 """parse a localized time string and return a (unixtime, offset) tuple.
1312 if the string cannot be parsed, ValueError is raised."""
1324 if the string cannot be parsed, ValueError is raised."""
1313 def timezone(string):
1325 def timezone(string):
1314 tz = string.split()[-1]
1326 tz = string.split()[-1]
1315 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1327 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1316 tz = int(tz)
1328 tz = int(tz)
1317 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1329 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1318 return offset
1330 return offset
1319 if tz == "GMT" or tz == "UTC":
1331 if tz == "GMT" or tz == "UTC":
1320 return 0
1332 return 0
1321 return None
1333 return None
1322
1334
1323 # NOTE: unixtime = localunixtime + offset
1335 # NOTE: unixtime = localunixtime + offset
1324 offset, date = timezone(string), string
1336 offset, date = timezone(string), string
1325 if offset != None:
1337 if offset != None:
1326 date = " ".join(string.split()[:-1])
1338 date = " ".join(string.split()[:-1])
1327
1339
1328 # add missing elements from defaults
1340 # add missing elements from defaults
1329 for part in defaults:
1341 for part in defaults:
1330 found = [True for p in part if ("%"+p) in format]
1342 found = [True for p in part if ("%"+p) in format]
1331 if not found:
1343 if not found:
1332 date += "@" + defaults[part]
1344 date += "@" + defaults[part]
1333 format += "@%" + part[0]
1345 format += "@%" + part[0]
1334
1346
1335 timetuple = time.strptime(date, format)
1347 timetuple = time.strptime(date, format)
1336 localunixtime = int(calendar.timegm(timetuple))
1348 localunixtime = int(calendar.timegm(timetuple))
1337 if offset is None:
1349 if offset is None:
1338 # local timezone
1350 # local timezone
1339 unixtime = int(time.mktime(timetuple))
1351 unixtime = int(time.mktime(timetuple))
1340 offset = unixtime - localunixtime
1352 offset = unixtime - localunixtime
1341 else:
1353 else:
1342 unixtime = localunixtime + offset
1354 unixtime = localunixtime + offset
1343 return unixtime, offset
1355 return unixtime, offset
1344
1356
1345 def parsedate(string, formats=None, defaults=None):
1357 def parsedate(string, formats=None, defaults=None):
1346 """parse a localized time string and return a (unixtime, offset) tuple.
1358 """parse a localized time string and return a (unixtime, offset) tuple.
1347 The date may be a "unixtime offset" string or in one of the specified
1359 The date may be a "unixtime offset" string or in one of the specified
1348 formats."""
1360 formats."""
1349 if not string:
1361 if not string:
1350 return 0, 0
1362 return 0, 0
1351 if not formats:
1363 if not formats:
1352 formats = defaultdateformats
1364 formats = defaultdateformats
1353 string = string.strip()
1365 string = string.strip()
1354 try:
1366 try:
1355 when, offset = map(int, string.split(' '))
1367 when, offset = map(int, string.split(' '))
1356 except ValueError:
1368 except ValueError:
1357 # fill out defaults
1369 # fill out defaults
1358 if not defaults:
1370 if not defaults:
1359 defaults = {}
1371 defaults = {}
1360 now = makedate()
1372 now = makedate()
1361 for part in "d mb yY HI M S".split():
1373 for part in "d mb yY HI M S".split():
1362 if part not in defaults:
1374 if part not in defaults:
1363 if part[0] in "HMS":
1375 if part[0] in "HMS":
1364 defaults[part] = "00"
1376 defaults[part] = "00"
1365 elif part[0] in "dm":
1377 elif part[0] in "dm":
1366 defaults[part] = "1"
1378 defaults[part] = "1"
1367 else:
1379 else:
1368 defaults[part] = datestr(now, "%" + part[0], False)
1380 defaults[part] = datestr(now, "%" + part[0], False)
1369
1381
1370 for format in formats:
1382 for format in formats:
1371 try:
1383 try:
1372 when, offset = strdate(string, format, defaults)
1384 when, offset = strdate(string, format, defaults)
1373 except ValueError:
1385 except ValueError:
1374 pass
1386 pass
1375 else:
1387 else:
1376 break
1388 break
1377 else:
1389 else:
1378 raise Abort(_('invalid date: %r ') % string)
1390 raise Abort(_('invalid date: %r ') % string)
1379 # validate explicit (probably user-specified) date and
1391 # validate explicit (probably user-specified) date and
1380 # time zone offset. values must fit in signed 32 bits for
1392 # time zone offset. values must fit in signed 32 bits for
1381 # current 32-bit linux runtimes. timezones go from UTC-12
1393 # current 32-bit linux runtimes. timezones go from UTC-12
1382 # to UTC+14
1394 # to UTC+14
1383 if abs(when) > 0x7fffffff:
1395 if abs(when) > 0x7fffffff:
1384 raise Abort(_('date exceeds 32 bits: %d') % when)
1396 raise Abort(_('date exceeds 32 bits: %d') % when)
1385 if offset < -50400 or offset > 43200:
1397 if offset < -50400 or offset > 43200:
1386 raise Abort(_('impossible time zone offset: %d') % offset)
1398 raise Abort(_('impossible time zone offset: %d') % offset)
1387 return when, offset
1399 return when, offset
1388
1400
1389 def matchdate(date):
1401 def matchdate(date):
1390 """Return a function that matches a given date match specifier
1402 """Return a function that matches a given date match specifier
1391
1403
1392 Formats include:
1404 Formats include:
1393
1405
1394 '{date}' match a given date to the accuracy provided
1406 '{date}' match a given date to the accuracy provided
1395
1407
1396 '<{date}' on or before a given date
1408 '<{date}' on or before a given date
1397
1409
1398 '>{date}' on or after a given date
1410 '>{date}' on or after a given date
1399
1411
1400 """
1412 """
1401
1413
1402 def lower(date):
1414 def lower(date):
1403 return parsedate(date, extendeddateformats)[0]
1415 return parsedate(date, extendeddateformats)[0]
1404
1416
1405 def upper(date):
1417 def upper(date):
1406 d = dict(mb="12", HI="23", M="59", S="59")
1418 d = dict(mb="12", HI="23", M="59", S="59")
1407 for days in "31 30 29".split():
1419 for days in "31 30 29".split():
1408 try:
1420 try:
1409 d["d"] = days
1421 d["d"] = days
1410 return parsedate(date, extendeddateformats, d)[0]
1422 return parsedate(date, extendeddateformats, d)[0]
1411 except:
1423 except:
1412 pass
1424 pass
1413 d["d"] = "28"
1425 d["d"] = "28"
1414 return parsedate(date, extendeddateformats, d)[0]
1426 return parsedate(date, extendeddateformats, d)[0]
1415
1427
1416 if date[0] == "<":
1428 if date[0] == "<":
1417 when = upper(date[1:])
1429 when = upper(date[1:])
1418 return lambda x: x <= when
1430 return lambda x: x <= when
1419 elif date[0] == ">":
1431 elif date[0] == ">":
1420 when = lower(date[1:])
1432 when = lower(date[1:])
1421 return lambda x: x >= when
1433 return lambda x: x >= when
1422 elif date[0] == "-":
1434 elif date[0] == "-":
1423 try:
1435 try:
1424 days = int(date[1:])
1436 days = int(date[1:])
1425 except ValueError:
1437 except ValueError:
1426 raise Abort(_("invalid day spec: %s") % date[1:])
1438 raise Abort(_("invalid day spec: %s") % date[1:])
1427 when = makedate()[0] - days * 3600 * 24
1439 when = makedate()[0] - days * 3600 * 24
1428 return lambda x: x >= when
1440 return lambda x: x >= when
1429 elif " to " in date:
1441 elif " to " in date:
1430 a, b = date.split(" to ")
1442 a, b = date.split(" to ")
1431 start, stop = lower(a), upper(b)
1443 start, stop = lower(a), upper(b)
1432 return lambda x: x >= start and x <= stop
1444 return lambda x: x >= start and x <= stop
1433 else:
1445 else:
1434 start, stop = lower(date), upper(date)
1446 start, stop = lower(date), upper(date)
1435 return lambda x: x >= start and x <= stop
1447 return lambda x: x >= start and x <= stop
1436
1448
1437 def shortuser(user):
1449 def shortuser(user):
1438 """Return a short representation of a user name or email address."""
1450 """Return a short representation of a user name or email address."""
1439 f = user.find('@')
1451 f = user.find('@')
1440 if f >= 0:
1452 if f >= 0:
1441 user = user[:f]
1453 user = user[:f]
1442 f = user.find('<')
1454 f = user.find('<')
1443 if f >= 0:
1455 if f >= 0:
1444 user = user[f+1:]
1456 user = user[f+1:]
1445 f = user.find(' ')
1457 f = user.find(' ')
1446 if f >= 0:
1458 if f >= 0:
1447 user = user[:f]
1459 user = user[:f]
1448 f = user.find('.')
1460 f = user.find('.')
1449 if f >= 0:
1461 if f >= 0:
1450 user = user[:f]
1462 user = user[:f]
1451 return user
1463 return user
1452
1464
1453 def ellipsis(text, maxlength=400):
1465 def ellipsis(text, maxlength=400):
1454 """Trim string to at most maxlength (default: 400) characters."""
1466 """Trim string to at most maxlength (default: 400) characters."""
1455 if len(text) <= maxlength:
1467 if len(text) <= maxlength:
1456 return text
1468 return text
1457 else:
1469 else:
1458 return "%s..." % (text[:maxlength-3])
1470 return "%s..." % (text[:maxlength-3])
1459
1471
1460 def walkrepos(path):
1472 def walkrepos(path):
1461 '''yield every hg repository under path, recursively.'''
1473 '''yield every hg repository under path, recursively.'''
1462 def errhandler(err):
1474 def errhandler(err):
1463 if err.filename == path:
1475 if err.filename == path:
1464 raise err
1476 raise err
1465
1477
1466 for root, dirs, files in os.walk(path, onerror=errhandler):
1478 for root, dirs, files in os.walk(path, onerror=errhandler):
1467 for d in dirs:
1479 for d in dirs:
1468 if d == '.hg':
1480 if d == '.hg':
1469 yield root
1481 yield root
1470 dirs[:] = []
1482 dirs[:] = []
1471 break
1483 break
1472
1484
1473 _rcpath = None
1485 _rcpath = None
1474
1486
1475 def os_rcpath():
1487 def os_rcpath():
1476 '''return default os-specific hgrc search path'''
1488 '''return default os-specific hgrc search path'''
1477 path = system_rcpath()
1489 path = system_rcpath()
1478 path.extend(user_rcpath())
1490 path.extend(user_rcpath())
1479 path = [os.path.normpath(f) for f in path]
1491 path = [os.path.normpath(f) for f in path]
1480 return path
1492 return path
1481
1493
1482 def rcpath():
1494 def rcpath():
1483 '''return hgrc search path. if env var HGRCPATH is set, use it.
1495 '''return hgrc search path. if env var HGRCPATH is set, use it.
1484 for each item in path, if directory, use files ending in .rc,
1496 for each item in path, if directory, use files ending in .rc,
1485 else use item.
1497 else use item.
1486 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1498 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1487 if no HGRCPATH, use default os-specific path.'''
1499 if no HGRCPATH, use default os-specific path.'''
1488 global _rcpath
1500 global _rcpath
1489 if _rcpath is None:
1501 if _rcpath is None:
1490 if 'HGRCPATH' in os.environ:
1502 if 'HGRCPATH' in os.environ:
1491 _rcpath = []
1503 _rcpath = []
1492 for p in os.environ['HGRCPATH'].split(os.pathsep):
1504 for p in os.environ['HGRCPATH'].split(os.pathsep):
1493 if not p: continue
1505 if not p: continue
1494 if os.path.isdir(p):
1506 if os.path.isdir(p):
1495 for f in os.listdir(p):
1507 for f in os.listdir(p):
1496 if f.endswith('.rc'):
1508 if f.endswith('.rc'):
1497 _rcpath.append(os.path.join(p, f))
1509 _rcpath.append(os.path.join(p, f))
1498 else:
1510 else:
1499 _rcpath.append(p)
1511 _rcpath.append(p)
1500 else:
1512 else:
1501 _rcpath = os_rcpath()
1513 _rcpath = os_rcpath()
1502 return _rcpath
1514 return _rcpath
1503
1515
1504 def bytecount(nbytes):
1516 def bytecount(nbytes):
1505 '''return byte count formatted as readable string, with units'''
1517 '''return byte count formatted as readable string, with units'''
1506
1518
1507 units = (
1519 units = (
1508 (100, 1<<30, _('%.0f GB')),
1520 (100, 1<<30, _('%.0f GB')),
1509 (10, 1<<30, _('%.1f GB')),
1521 (10, 1<<30, _('%.1f GB')),
1510 (1, 1<<30, _('%.2f GB')),
1522 (1, 1<<30, _('%.2f GB')),
1511 (100, 1<<20, _('%.0f MB')),
1523 (100, 1<<20, _('%.0f MB')),
1512 (10, 1<<20, _('%.1f MB')),
1524 (10, 1<<20, _('%.1f MB')),
1513 (1, 1<<20, _('%.2f MB')),
1525 (1, 1<<20, _('%.2f MB')),
1514 (100, 1<<10, _('%.0f KB')),
1526 (100, 1<<10, _('%.0f KB')),
1515 (10, 1<<10, _('%.1f KB')),
1527 (10, 1<<10, _('%.1f KB')),
1516 (1, 1<<10, _('%.2f KB')),
1528 (1, 1<<10, _('%.2f KB')),
1517 (1, 1, _('%.0f bytes')),
1529 (1, 1, _('%.0f bytes')),
1518 )
1530 )
1519
1531
1520 for multiplier, divisor, format in units:
1532 for multiplier, divisor, format in units:
1521 if nbytes >= divisor * multiplier:
1533 if nbytes >= divisor * multiplier:
1522 return format % (nbytes / float(divisor))
1534 return format % (nbytes / float(divisor))
1523 return units[-1][2] % nbytes
1535 return units[-1][2] % nbytes
1524
1536
1525 def drop_scheme(scheme, path):
1537 def drop_scheme(scheme, path):
1526 sc = scheme + ':'
1538 sc = scheme + ':'
1527 if path.startswith(sc):
1539 if path.startswith(sc):
1528 path = path[len(sc):]
1540 path = path[len(sc):]
1529 if path.startswith('//'):
1541 if path.startswith('//'):
1530 path = path[2:]
1542 path = path[2:]
1531 return path
1543 return path
General Comments 0
You need to be logged in to leave comments. Login now