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