##// END OF EJS Templates
Merge with crew-stable
Patrick Mezard -
r4806:79210a63 merge default
parent child Browse files
Show More
@@ -1,3125 +1,3125
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 import demandimport; demandimport.enable()
8 import demandimport; demandimport.enable()
9 from node import *
9 from node import *
10 from i18n import _
10 from i18n import _
11 import bisect, os, re, sys, urllib, shlex, stat
11 import bisect, os, re, sys, urllib, shlex, stat
12 import ui, hg, util, revlog, bundlerepo, extensions
12 import ui, hg, util, revlog, bundlerepo, extensions
13 import difflib, patch, time, help, mdiff, tempfile
13 import difflib, patch, time, help, mdiff, tempfile
14 import errno, version, socket
14 import errno, version, socket
15 import archival, changegroup, cmdutil, hgweb.server, sshserver
15 import archival, changegroup, cmdutil, hgweb.server, sshserver
16
16
17 # Commands start here, listed alphabetically
17 # Commands start here, listed alphabetically
18
18
19 def add(ui, repo, *pats, **opts):
19 def add(ui, repo, *pats, **opts):
20 """add the specified files on the next commit
20 """add the specified files on the next commit
21
21
22 Schedule files to be version controlled and added to the repository.
22 Schedule files to be version controlled and added to the repository.
23
23
24 The files will be added to the repository at the next commit. To
24 The files will be added to the repository at the next commit. To
25 undo an add before that, see hg revert.
25 undo an add before that, see hg revert.
26
26
27 If no names are given, add all files in the repository.
27 If no names are given, add all files in the repository.
28 """
28 """
29
29
30 names = []
30 names = []
31 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
31 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
32 if exact:
32 if exact:
33 if ui.verbose:
33 if ui.verbose:
34 ui.status(_('adding %s\n') % rel)
34 ui.status(_('adding %s\n') % rel)
35 names.append(abs)
35 names.append(abs)
36 elif repo.dirstate.state(abs) == '?':
36 elif repo.dirstate.state(abs) == '?':
37 ui.status(_('adding %s\n') % rel)
37 ui.status(_('adding %s\n') % rel)
38 names.append(abs)
38 names.append(abs)
39 if not opts.get('dry_run'):
39 if not opts.get('dry_run'):
40 repo.add(names)
40 repo.add(names)
41
41
42 def addremove(ui, repo, *pats, **opts):
42 def addremove(ui, repo, *pats, **opts):
43 """add all new files, delete all missing files
43 """add all new files, delete all missing files
44
44
45 Add all new files and remove all missing files from the repository.
45 Add all new files and remove all missing files from the repository.
46
46
47 New files are ignored if they match any of the patterns in .hgignore. As
47 New files are ignored if they match any of the patterns in .hgignore. As
48 with add, these changes take effect at the next commit.
48 with add, these changes take effect at the next commit.
49
49
50 Use the -s option to detect renamed files. With a parameter > 0,
50 Use the -s option to detect renamed files. With a parameter > 0,
51 this compares every removed file with every added file and records
51 this compares every removed file with every added file and records
52 those similar enough as renames. This option takes a percentage
52 those similar enough as renames. This option takes a percentage
53 between 0 (disabled) and 100 (files must be identical) as its
53 between 0 (disabled) and 100 (files must be identical) as its
54 parameter. Detecting renamed files this way can be expensive.
54 parameter. Detecting renamed files this way can be expensive.
55 """
55 """
56 sim = float(opts.get('similarity') or 0)
56 sim = float(opts.get('similarity') or 0)
57 if sim < 0 or sim > 100:
57 if sim < 0 or sim > 100:
58 raise util.Abort(_('similarity must be between 0 and 100'))
58 raise util.Abort(_('similarity must be between 0 and 100'))
59 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
59 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
60
60
61 def annotate(ui, repo, *pats, **opts):
61 def annotate(ui, repo, *pats, **opts):
62 """show changeset information per file line
62 """show changeset information per file line
63
63
64 List changes in files, showing the revision id responsible for each line
64 List changes in files, showing the revision id responsible for each line
65
65
66 This command is useful to discover who did a change or when a change took
66 This command is useful to discover who did a change or when a change took
67 place.
67 place.
68
68
69 Without the -a option, annotate will avoid processing files it
69 Without the -a option, annotate will avoid processing files it
70 detects as binary. With -a, annotate will generate an annotation
70 detects as binary. With -a, annotate will generate an annotation
71 anyway, probably with undesirable results.
71 anyway, probably with undesirable results.
72 """
72 """
73 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
73 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
74
74
75 if not pats:
75 if not pats:
76 raise util.Abort(_('at least one file name or pattern required'))
76 raise util.Abort(_('at least one file name or pattern required'))
77
77
78 opmap = [['user', lambda x: ui.shortuser(x.user())],
78 opmap = [['user', lambda x: ui.shortuser(x.user())],
79 ['number', lambda x: str(x.rev())],
79 ['number', lambda x: str(x.rev())],
80 ['changeset', lambda x: short(x.node())],
80 ['changeset', lambda x: short(x.node())],
81 ['date', getdate], ['follow', lambda x: x.path()]]
81 ['date', getdate], ['follow', lambda x: x.path()]]
82 if (not opts['user'] and not opts['changeset'] and not opts['date']
82 if (not opts['user'] and not opts['changeset'] and not opts['date']
83 and not opts['follow']):
83 and not opts['follow']):
84 opts['number'] = 1
84 opts['number'] = 1
85
85
86 ctx = repo.changectx(opts['rev'])
86 ctx = repo.changectx(opts['rev'])
87
87
88 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
88 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
89 node=ctx.node()):
89 node=ctx.node()):
90 fctx = ctx.filectx(abs)
90 fctx = ctx.filectx(abs)
91 if not opts['text'] and util.binary(fctx.data()):
91 if not opts['text'] and util.binary(fctx.data()):
92 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
92 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
93 continue
93 continue
94
94
95 lines = fctx.annotate(follow=opts.get('follow'))
95 lines = fctx.annotate(follow=opts.get('follow'))
96 pieces = []
96 pieces = []
97
97
98 for o, f in opmap:
98 for o, f in opmap:
99 if opts[o]:
99 if opts[o]:
100 l = [f(n) for n, dummy in lines]
100 l = [f(n) for n, dummy in lines]
101 if l:
101 if l:
102 m = max(map(len, l))
102 m = max(map(len, l))
103 pieces.append(["%*s" % (m, x) for x in l])
103 pieces.append(["%*s" % (m, x) for x in l])
104
104
105 if pieces:
105 if pieces:
106 for p, l in zip(zip(*pieces), lines):
106 for p, l in zip(zip(*pieces), lines):
107 ui.write("%s: %s" % (" ".join(p), l[1]))
107 ui.write("%s: %s" % (" ".join(p), l[1]))
108
108
109 def archive(ui, repo, dest, **opts):
109 def archive(ui, repo, dest, **opts):
110 '''create unversioned archive of a repository revision
110 '''create unversioned archive of a repository revision
111
111
112 By default, the revision used is the parent of the working
112 By default, the revision used is the parent of the working
113 directory; use "-r" to specify a different revision.
113 directory; use "-r" to specify a different revision.
114
114
115 To specify the type of archive to create, use "-t". Valid
115 To specify the type of archive to create, use "-t". Valid
116 types are:
116 types are:
117
117
118 "files" (default): a directory full of files
118 "files" (default): a directory full of files
119 "tar": tar archive, uncompressed
119 "tar": tar archive, uncompressed
120 "tbz2": tar archive, compressed using bzip2
120 "tbz2": tar archive, compressed using bzip2
121 "tgz": tar archive, compressed using gzip
121 "tgz": tar archive, compressed using gzip
122 "uzip": zip archive, uncompressed
122 "uzip": zip archive, uncompressed
123 "zip": zip archive, compressed using deflate
123 "zip": zip archive, compressed using deflate
124
124
125 The exact name of the destination archive or directory is given
125 The exact name of the destination archive or directory is given
126 using a format string; see "hg help export" for details.
126 using a format string; see "hg help export" for details.
127
127
128 Each member added to an archive file has a directory prefix
128 Each member added to an archive file has a directory prefix
129 prepended. Use "-p" to specify a format string for the prefix.
129 prepended. Use "-p" to specify a format string for the prefix.
130 The default is the basename of the archive, with suffixes removed.
130 The default is the basename of the archive, with suffixes removed.
131 '''
131 '''
132
132
133 node = repo.changectx(opts['rev']).node()
133 node = repo.changectx(opts['rev']).node()
134 dest = cmdutil.make_filename(repo, dest, node)
134 dest = cmdutil.make_filename(repo, dest, node)
135 if os.path.realpath(dest) == repo.root:
135 if os.path.realpath(dest) == repo.root:
136 raise util.Abort(_('repository root cannot be destination'))
136 raise util.Abort(_('repository root cannot be destination'))
137 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
137 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
138 kind = opts.get('type') or 'files'
138 kind = opts.get('type') or 'files'
139 prefix = opts['prefix']
139 prefix = opts['prefix']
140 if dest == '-':
140 if dest == '-':
141 if kind == 'files':
141 if kind == 'files':
142 raise util.Abort(_('cannot archive plain files to stdout'))
142 raise util.Abort(_('cannot archive plain files to stdout'))
143 dest = sys.stdout
143 dest = sys.stdout
144 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
144 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
145 prefix = cmdutil.make_filename(repo, prefix, node)
145 prefix = cmdutil.make_filename(repo, prefix, node)
146 archival.archive(repo, dest, node, kind, not opts['no_decode'],
146 archival.archive(repo, dest, node, kind, not opts['no_decode'],
147 matchfn, prefix)
147 matchfn, prefix)
148
148
149 def backout(ui, repo, node=None, rev=None, **opts):
149 def backout(ui, repo, node=None, rev=None, **opts):
150 '''reverse effect of earlier changeset
150 '''reverse effect of earlier changeset
151
151
152 Commit the backed out changes as a new changeset. The new
152 Commit the backed out changes as a new changeset. The new
153 changeset is a child of the backed out changeset.
153 changeset is a child of the backed out changeset.
154
154
155 If you back out a changeset other than the tip, a new head is
155 If you back out a changeset other than the tip, a new head is
156 created. This head is the parent of the working directory. If
156 created. This head is the parent of the working directory. If
157 you back out an old changeset, your working directory will appear
157 you back out an old changeset, your working directory will appear
158 old after the backout. You should merge the backout changeset
158 old after the backout. You should merge the backout changeset
159 with another head.
159 with another head.
160
160
161 The --merge option remembers the parent of the working directory
161 The --merge option remembers the parent of the working directory
162 before starting the backout, then merges the new head with that
162 before starting the backout, then merges the new head with that
163 changeset afterwards. This saves you from doing the merge by
163 changeset afterwards. This saves you from doing the merge by
164 hand. The result of this merge is not committed, as for a normal
164 hand. The result of this merge is not committed, as for a normal
165 merge.'''
165 merge.'''
166 if rev and node:
166 if rev and node:
167 raise util.Abort(_("please specify just one revision"))
167 raise util.Abort(_("please specify just one revision"))
168
168
169 if not rev:
169 if not rev:
170 rev = node
170 rev = node
171
171
172 if not rev:
172 if not rev:
173 raise util.Abort(_("please specify a revision to backout"))
173 raise util.Abort(_("please specify a revision to backout"))
174
174
175 cmdutil.bail_if_changed(repo)
175 cmdutil.bail_if_changed(repo)
176 op1, op2 = repo.dirstate.parents()
176 op1, op2 = repo.dirstate.parents()
177 if op2 != nullid:
177 if op2 != nullid:
178 raise util.Abort(_('outstanding uncommitted merge'))
178 raise util.Abort(_('outstanding uncommitted merge'))
179 node = repo.lookup(rev)
179 node = repo.lookup(rev)
180 p1, p2 = repo.changelog.parents(node)
180 p1, p2 = repo.changelog.parents(node)
181 if p1 == nullid:
181 if p1 == nullid:
182 raise util.Abort(_('cannot back out a change with no parents'))
182 raise util.Abort(_('cannot back out a change with no parents'))
183 if p2 != nullid:
183 if p2 != nullid:
184 if not opts['parent']:
184 if not opts['parent']:
185 raise util.Abort(_('cannot back out a merge changeset without '
185 raise util.Abort(_('cannot back out a merge changeset without '
186 '--parent'))
186 '--parent'))
187 p = repo.lookup(opts['parent'])
187 p = repo.lookup(opts['parent'])
188 if p not in (p1, p2):
188 if p not in (p1, p2):
189 raise util.Abort(_('%s is not a parent of %s') %
189 raise util.Abort(_('%s is not a parent of %s') %
190 (short(p), short(node)))
190 (short(p), short(node)))
191 parent = p
191 parent = p
192 else:
192 else:
193 if opts['parent']:
193 if opts['parent']:
194 raise util.Abort(_('cannot use --parent on non-merge changeset'))
194 raise util.Abort(_('cannot use --parent on non-merge changeset'))
195 parent = p1
195 parent = p1
196 hg.clean(repo, node, show_stats=False)
196 hg.clean(repo, node, show_stats=False)
197 revert_opts = opts.copy()
197 revert_opts = opts.copy()
198 revert_opts['date'] = None
198 revert_opts['date'] = None
199 revert_opts['all'] = True
199 revert_opts['all'] = True
200 revert_opts['rev'] = hex(parent)
200 revert_opts['rev'] = hex(parent)
201 revert(ui, repo, **revert_opts)
201 revert(ui, repo, **revert_opts)
202 commit_opts = opts.copy()
202 commit_opts = opts.copy()
203 commit_opts['addremove'] = False
203 commit_opts['addremove'] = False
204 if not commit_opts['message'] and not commit_opts['logfile']:
204 if not commit_opts['message'] and not commit_opts['logfile']:
205 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
205 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
206 commit_opts['force_editor'] = True
206 commit_opts['force_editor'] = True
207 commit(ui, repo, **commit_opts)
207 commit(ui, repo, **commit_opts)
208 def nice(node):
208 def nice(node):
209 return '%d:%s' % (repo.changelog.rev(node), short(node))
209 return '%d:%s' % (repo.changelog.rev(node), short(node))
210 ui.status(_('changeset %s backs out changeset %s\n') %
210 ui.status(_('changeset %s backs out changeset %s\n') %
211 (nice(repo.changelog.tip()), nice(node)))
211 (nice(repo.changelog.tip()), nice(node)))
212 if op1 != node:
212 if op1 != node:
213 if opts['merge']:
213 if opts['merge']:
214 ui.status(_('merging with changeset %s\n') % nice(op1))
214 ui.status(_('merging with changeset %s\n') % nice(op1))
215 hg.merge(repo, hex(op1))
215 hg.merge(repo, hex(op1))
216 else:
216 else:
217 ui.status(_('the backout changeset is a new head - '
217 ui.status(_('the backout changeset is a new head - '
218 'do not forget to merge\n'))
218 'do not forget to merge\n'))
219 ui.status(_('(use "backout --merge" '
219 ui.status(_('(use "backout --merge" '
220 'if you want to auto-merge)\n'))
220 'if you want to auto-merge)\n'))
221
221
222 def branch(ui, repo, label=None, **opts):
222 def branch(ui, repo, label=None, **opts):
223 """set or show the current branch name
223 """set or show the current branch name
224
224
225 With no argument, show the current branch name. With one argument,
225 With no argument, show the current branch name. With one argument,
226 set the working directory branch name (the branch does not exist in
226 set the working directory branch name (the branch does not exist in
227 the repository until the next commit).
227 the repository until the next commit).
228
228
229 Unless --force is specified, branch will not let you set a
229 Unless --force is specified, branch will not let you set a
230 branch name that shadows an existing branch.
230 branch name that shadows an existing branch.
231 """
231 """
232
232
233 if label:
233 if label:
234 if not opts.get('force') and label in repo.branchtags():
234 if not opts.get('force') and label in repo.branchtags():
235 if label not in [p.branch() for p in repo.workingctx().parents()]:
235 if label not in [p.branch() for p in repo.workingctx().parents()]:
236 raise util.Abort(_('a branch of the same name already exists'
236 raise util.Abort(_('a branch of the same name already exists'
237 ' (use --force to override)'))
237 ' (use --force to override)'))
238 repo.dirstate.setbranch(util.fromlocal(label))
238 repo.dirstate.setbranch(util.fromlocal(label))
239 ui.status(_('marked working directory as branch %s\n') % label)
239 ui.status(_('marked working directory as branch %s\n') % label)
240 else:
240 else:
241 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
241 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
242
242
243 def branches(ui, repo, active=False):
243 def branches(ui, repo, active=False):
244 """list repository named branches
244 """list repository named branches
245
245
246 List the repository's named branches, indicating which ones are
246 List the repository's named branches, indicating which ones are
247 inactive. If active is specified, only show active branches.
247 inactive. If active is specified, only show active branches.
248
248
249 A branch is considered active if it contains unmerged heads.
249 A branch is considered active if it contains unmerged heads.
250 """
250 """
251 b = repo.branchtags()
251 b = repo.branchtags()
252 heads = dict.fromkeys(repo.heads(), 1)
252 heads = dict.fromkeys(repo.heads(), 1)
253 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
253 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
254 l.sort()
254 l.sort()
255 l.reverse()
255 l.reverse()
256 for ishead, r, n, t in l:
256 for ishead, r, n, t in l:
257 if active and not ishead:
257 if active and not ishead:
258 # If we're only displaying active branches, abort the loop on
258 # If we're only displaying active branches, abort the loop on
259 # encountering the first inactive head
259 # encountering the first inactive head
260 break
260 break
261 else:
261 else:
262 hexfunc = ui.debugflag and hex or short
262 hexfunc = ui.debugflag and hex or short
263 if ui.quiet:
263 if ui.quiet:
264 ui.write("%s\n" % t)
264 ui.write("%s\n" % t)
265 else:
265 else:
266 spaces = " " * (30 - util.locallen(t))
266 spaces = " " * (30 - util.locallen(t))
267 # The code only gets here if inactive branches are being
267 # The code only gets here if inactive branches are being
268 # displayed or the branch is active.
268 # displayed or the branch is active.
269 isinactive = ((not ishead) and " (inactive)") or ''
269 isinactive = ((not ishead) and " (inactive)") or ''
270 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
270 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
271
271
272 def bundle(ui, repo, fname, dest=None, **opts):
272 def bundle(ui, repo, fname, dest=None, **opts):
273 """create a changegroup file
273 """create a changegroup file
274
274
275 Generate a compressed changegroup file collecting changesets not
275 Generate a compressed changegroup file collecting changesets not
276 found in the other repository.
276 found in the other repository.
277
277
278 If no destination repository is specified the destination is assumed
278 If no destination repository is specified the destination is assumed
279 to have all the nodes specified by one or more --base parameters.
279 to have all the nodes specified by one or more --base parameters.
280
280
281 The bundle file can then be transferred using conventional means and
281 The bundle file can then be transferred using conventional means and
282 applied to another repository with the unbundle or pull command.
282 applied to another repository with the unbundle or pull command.
283 This is useful when direct push and pull are not available or when
283 This is useful when direct push and pull are not available or when
284 exporting an entire repository is undesirable.
284 exporting an entire repository is undesirable.
285
285
286 Applying bundles preserves all changeset contents including
286 Applying bundles preserves all changeset contents including
287 permissions, copy/rename information, and revision history.
287 permissions, copy/rename information, and revision history.
288 """
288 """
289 revs = opts.get('rev') or None
289 revs = opts.get('rev') or None
290 if revs:
290 if revs:
291 revs = [repo.lookup(rev) for rev in revs]
291 revs = [repo.lookup(rev) for rev in revs]
292 base = opts.get('base')
292 base = opts.get('base')
293 if base:
293 if base:
294 if dest:
294 if dest:
295 raise util.Abort(_("--base is incompatible with specifiying "
295 raise util.Abort(_("--base is incompatible with specifiying "
296 "a destination"))
296 "a destination"))
297 base = [repo.lookup(rev) for rev in base]
297 base = [repo.lookup(rev) for rev in base]
298 # create the right base
298 # create the right base
299 # XXX: nodesbetween / changegroup* should be "fixed" instead
299 # XXX: nodesbetween / changegroup* should be "fixed" instead
300 o = []
300 o = []
301 has = {nullid: None}
301 has = {nullid: None}
302 for n in base:
302 for n in base:
303 has.update(repo.changelog.reachable(n))
303 has.update(repo.changelog.reachable(n))
304 if revs:
304 if revs:
305 visit = list(revs)
305 visit = list(revs)
306 else:
306 else:
307 visit = repo.changelog.heads()
307 visit = repo.changelog.heads()
308 seen = {}
308 seen = {}
309 while visit:
309 while visit:
310 n = visit.pop(0)
310 n = visit.pop(0)
311 parents = [p for p in repo.changelog.parents(n) if p not in has]
311 parents = [p for p in repo.changelog.parents(n) if p not in has]
312 if len(parents) == 0:
312 if len(parents) == 0:
313 o.insert(0, n)
313 o.insert(0, n)
314 else:
314 else:
315 for p in parents:
315 for p in parents:
316 if p not in seen:
316 if p not in seen:
317 seen[p] = 1
317 seen[p] = 1
318 visit.append(p)
318 visit.append(p)
319 else:
319 else:
320 cmdutil.setremoteconfig(ui, opts)
320 cmdutil.setremoteconfig(ui, opts)
321 dest, revs = cmdutil.parseurl(
321 dest, revs = cmdutil.parseurl(
322 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
322 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
323 other = hg.repository(ui, dest)
323 other = hg.repository(ui, dest)
324 o = repo.findoutgoing(other, force=opts['force'])
324 o = repo.findoutgoing(other, force=opts['force'])
325
325
326 if revs:
326 if revs:
327 cg = repo.changegroupsubset(o, revs, 'bundle')
327 cg = repo.changegroupsubset(o, revs, 'bundle')
328 else:
328 else:
329 cg = repo.changegroup(o, 'bundle')
329 cg = repo.changegroup(o, 'bundle')
330 changegroup.writebundle(cg, fname, "HG10BZ")
330 changegroup.writebundle(cg, fname, "HG10BZ")
331
331
332 def cat(ui, repo, file1, *pats, **opts):
332 def cat(ui, repo, file1, *pats, **opts):
333 """output the current or given revision of files
333 """output the current or given revision of files
334
334
335 Print the specified files as they were at the given revision.
335 Print the specified files as they were at the given revision.
336 If no revision is given, the parent of the working directory is used,
336 If no revision is given, the parent of the working directory is used,
337 or tip if no revision is checked out.
337 or tip if no revision is checked out.
338
338
339 Output may be to a file, in which case the name of the file is
339 Output may be to a file, in which case the name of the file is
340 given using a format string. The formatting rules are the same as
340 given using a format string. The formatting rules are the same as
341 for the export command, with the following additions:
341 for the export command, with the following additions:
342
342
343 %s basename of file being printed
343 %s basename of file being printed
344 %d dirname of file being printed, or '.' if in repo root
344 %d dirname of file being printed, or '.' if in repo root
345 %p root-relative path name of file being printed
345 %p root-relative path name of file being printed
346 """
346 """
347 ctx = repo.changectx(opts['rev'])
347 ctx = repo.changectx(opts['rev'])
348 err = 1
348 err = 1
349 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
349 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
350 ctx.node()):
350 ctx.node()):
351 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
351 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
352 fp.write(ctx.filectx(abs).data())
352 fp.write(ctx.filectx(abs).data())
353 err = 0
353 err = 0
354 return err
354 return err
355
355
356 def clone(ui, source, dest=None, **opts):
356 def clone(ui, source, dest=None, **opts):
357 """make a copy of an existing repository
357 """make a copy of an existing repository
358
358
359 Create a copy of an existing repository in a new directory.
359 Create a copy of an existing repository in a new directory.
360
360
361 If no destination directory name is specified, it defaults to the
361 If no destination directory name is specified, it defaults to the
362 basename of the source.
362 basename of the source.
363
363
364 The location of the source is added to the new repository's
364 The location of the source is added to the new repository's
365 .hg/hgrc file, as the default to be used for future pulls.
365 .hg/hgrc file, as the default to be used for future pulls.
366
366
367 For efficiency, hardlinks are used for cloning whenever the source
367 For efficiency, hardlinks are used for cloning whenever the source
368 and destination are on the same filesystem (note this applies only
368 and destination are on the same filesystem (note this applies only
369 to the repository data, not to the checked out files). Some
369 to the repository data, not to the checked out files). Some
370 filesystems, such as AFS, implement hardlinking incorrectly, but
370 filesystems, such as AFS, implement hardlinking incorrectly, but
371 do not report errors. In these cases, use the --pull option to
371 do not report errors. In these cases, use the --pull option to
372 avoid hardlinking.
372 avoid hardlinking.
373
373
374 You can safely clone repositories and checked out files using full
374 You can safely clone repositories and checked out files using full
375 hardlinks with
375 hardlinks with
376
376
377 $ cp -al REPO REPOCLONE
377 $ cp -al REPO REPOCLONE
378
378
379 which is the fastest way to clone. However, the operation is not
379 which is the fastest way to clone. However, the operation is not
380 atomic (making sure REPO is not modified during the operation is
380 atomic (making sure REPO is not modified during the operation is
381 up to you) and you have to make sure your editor breaks hardlinks
381 up to you) and you have to make sure your editor breaks hardlinks
382 (Emacs and most Linux Kernel tools do so).
382 (Emacs and most Linux Kernel tools do so).
383
383
384 If you use the -r option to clone up to a specific revision, no
384 If you use the -r option to clone up to a specific revision, no
385 subsequent revisions will be present in the cloned repository.
385 subsequent revisions will be present in the cloned repository.
386 This option implies --pull, even on local repositories.
386 This option implies --pull, even on local repositories.
387
387
388 See pull for valid source format details.
388 See pull for valid source format details.
389
389
390 It is possible to specify an ssh:// URL as the destination, but no
390 It is possible to specify an ssh:// URL as the destination, but no
391 .hg/hgrc and working directory will be created on the remote side.
391 .hg/hgrc and working directory will be created on the remote side.
392 Look at the help text for the pull command for important details
392 Look at the help text for the pull command for important details
393 about ssh:// URLs.
393 about ssh:// URLs.
394 """
394 """
395 cmdutil.setremoteconfig(ui, opts)
395 cmdutil.setremoteconfig(ui, opts)
396 hg.clone(ui, source, dest,
396 hg.clone(ui, source, dest,
397 pull=opts['pull'],
397 pull=opts['pull'],
398 stream=opts['uncompressed'],
398 stream=opts['uncompressed'],
399 rev=opts['rev'],
399 rev=opts['rev'],
400 update=not opts['noupdate'])
400 update=not opts['noupdate'])
401
401
402 def commit(ui, repo, *pats, **opts):
402 def commit(ui, repo, *pats, **opts):
403 """commit the specified files or all outstanding changes
403 """commit the specified files or all outstanding changes
404
404
405 Commit changes to the given files into the repository.
405 Commit changes to the given files into the repository.
406
406
407 If a list of files is omitted, all changes reported by "hg status"
407 If a list of files is omitted, all changes reported by "hg status"
408 will be committed.
408 will be committed.
409
409
410 If no commit message is specified, the editor configured in your hgrc
410 If no commit message is specified, the editor configured in your hgrc
411 or in the EDITOR environment variable is started to enter a message.
411 or in the EDITOR environment variable is started to enter a message.
412 """
412 """
413 message = cmdutil.logmessage(opts)
413 message = cmdutil.logmessage(opts)
414
414
415 if opts['addremove']:
415 if opts['addremove']:
416 cmdutil.addremove(repo, pats, opts)
416 cmdutil.addremove(repo, pats, opts)
417 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
417 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
418 if pats:
418 if pats:
419 status = repo.status(files=fns, match=match)
419 status = repo.status(files=fns, match=match)
420 modified, added, removed, deleted, unknown = status[:5]
420 modified, added, removed, deleted, unknown = status[:5]
421 files = modified + added + removed
421 files = modified + added + removed
422 slist = None
422 slist = None
423 for f in fns:
423 for f in fns:
424 if f == '.':
424 if f == '.':
425 continue
425 continue
426 if f not in files:
426 if f not in files:
427 rf = repo.wjoin(f)
427 rf = repo.wjoin(f)
428 try:
428 try:
429 mode = os.lstat(rf)[stat.ST_MODE]
429 mode = os.lstat(rf)[stat.ST_MODE]
430 except OSError:
430 except OSError:
431 raise util.Abort(_("file %s not found!") % rf)
431 raise util.Abort(_("file %s not found!") % rf)
432 if stat.S_ISDIR(mode):
432 if stat.S_ISDIR(mode):
433 name = f + '/'
433 name = f + '/'
434 if slist is None:
434 if slist is None:
435 slist = list(files)
435 slist = list(files)
436 slist.sort()
436 slist.sort()
437 i = bisect.bisect(slist, name)
437 i = bisect.bisect(slist, name)
438 if i >= len(slist) or not slist[i].startswith(name):
438 if i >= len(slist) or not slist[i].startswith(name):
439 raise util.Abort(_("no match under directory %s!")
439 raise util.Abort(_("no match under directory %s!")
440 % rf)
440 % rf)
441 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
441 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
442 raise util.Abort(_("can't commit %s: "
442 raise util.Abort(_("can't commit %s: "
443 "unsupported file type!") % rf)
443 "unsupported file type!") % rf)
444 elif repo.dirstate.state(f) == '?':
444 elif repo.dirstate.state(f) == '?':
445 raise util.Abort(_("file %s not tracked!") % rf)
445 raise util.Abort(_("file %s not tracked!") % rf)
446 else:
446 else:
447 files = []
447 files = []
448 try:
448 try:
449 repo.commit(files, message, opts['user'], opts['date'], match,
449 repo.commit(files, message, opts['user'], opts['date'], match,
450 force_editor=opts.get('force_editor'))
450 force_editor=opts.get('force_editor'))
451 except ValueError, inst:
451 except ValueError, inst:
452 raise util.Abort(str(inst))
452 raise util.Abort(str(inst))
453
453
454 def docopy(ui, repo, pats, opts, wlock):
454 def docopy(ui, repo, pats, opts, wlock):
455 # called with the repo lock held
455 # called with the repo lock held
456 #
456 #
457 # hgsep => pathname that uses "/" to separate directories
457 # hgsep => pathname that uses "/" to separate directories
458 # ossep => pathname that uses os.sep to separate directories
458 # ossep => pathname that uses os.sep to separate directories
459 cwd = repo.getcwd()
459 cwd = repo.getcwd()
460 errors = 0
460 errors = 0
461 copied = []
461 copied = []
462 targets = {}
462 targets = {}
463
463
464 # abs: hgsep
464 # abs: hgsep
465 # rel: ossep
465 # rel: ossep
466 # return: hgsep
466 # return: hgsep
467 def okaytocopy(abs, rel, exact):
467 def okaytocopy(abs, rel, exact):
468 reasons = {'?': _('is not managed'),
468 reasons = {'?': _('is not managed'),
469 'r': _('has been marked for remove')}
469 'r': _('has been marked for remove')}
470 state = repo.dirstate.state(abs)
470 state = repo.dirstate.state(abs)
471 reason = reasons.get(state)
471 reason = reasons.get(state)
472 if reason:
472 if reason:
473 if exact:
473 if exact:
474 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
474 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
475 else:
475 else:
476 if state == 'a':
476 if state == 'a':
477 origsrc = repo.dirstate.copied(abs)
477 origsrc = repo.dirstate.copied(abs)
478 if origsrc is not None:
478 if origsrc is not None:
479 return origsrc
479 return origsrc
480 return abs
480 return abs
481
481
482 # origsrc: hgsep
482 # origsrc: hgsep
483 # abssrc: hgsep
483 # abssrc: hgsep
484 # relsrc: ossep
484 # relsrc: ossep
485 # otarget: ossep
485 # otarget: ossep
486 def copy(origsrc, abssrc, relsrc, otarget, exact):
486 def copy(origsrc, abssrc, relsrc, otarget, exact):
487 abstarget = util.canonpath(repo.root, cwd, otarget)
487 abstarget = util.canonpath(repo.root, cwd, otarget)
488 reltarget = repo.pathto(abstarget, cwd)
488 reltarget = repo.pathto(abstarget, cwd)
489 prevsrc = targets.get(abstarget)
489 prevsrc = targets.get(abstarget)
490 src = repo.wjoin(abssrc)
490 src = repo.wjoin(abssrc)
491 target = repo.wjoin(abstarget)
491 target = repo.wjoin(abstarget)
492 if prevsrc is not None:
492 if prevsrc is not None:
493 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
493 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
494 (reltarget, repo.pathto(abssrc, cwd),
494 (reltarget, repo.pathto(abssrc, cwd),
495 repo.pathto(prevsrc, cwd)))
495 repo.pathto(prevsrc, cwd)))
496 return
496 return
497 if (not opts['after'] and os.path.exists(target) or
497 if (not opts['after'] and os.path.exists(target) or
498 opts['after'] and repo.dirstate.state(abstarget) not in '?ar'):
498 opts['after'] and repo.dirstate.state(abstarget) not in '?ar'):
499 if not opts['force']:
499 if not opts['force']:
500 ui.warn(_('%s: not overwriting - file exists\n') %
500 ui.warn(_('%s: not overwriting - file exists\n') %
501 reltarget)
501 reltarget)
502 return
502 return
503 if not opts['after'] and not opts.get('dry_run'):
503 if not opts['after'] and not opts.get('dry_run'):
504 os.unlink(target)
504 os.unlink(target)
505 if opts['after']:
505 if opts['after']:
506 if not os.path.exists(target):
506 if not os.path.exists(target):
507 return
507 return
508 else:
508 else:
509 targetdir = os.path.dirname(target) or '.'
509 targetdir = os.path.dirname(target) or '.'
510 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
510 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
511 os.makedirs(targetdir)
511 os.makedirs(targetdir)
512 try:
512 try:
513 restore = repo.dirstate.state(abstarget) == 'r'
513 restore = repo.dirstate.state(abstarget) == 'r'
514 if restore and not opts.get('dry_run'):
514 if restore and not opts.get('dry_run'):
515 repo.undelete([abstarget], wlock)
515 repo.undelete([abstarget], wlock)
516 try:
516 try:
517 if not opts.get('dry_run'):
517 if not opts.get('dry_run'):
518 util.copyfile(src, target)
518 util.copyfile(src, target)
519 restore = False
519 restore = False
520 finally:
520 finally:
521 if restore:
521 if restore:
522 repo.remove([abstarget], wlock=wlock)
522 repo.remove([abstarget], wlock=wlock)
523 except IOError, inst:
523 except IOError, inst:
524 if inst.errno == errno.ENOENT:
524 if inst.errno == errno.ENOENT:
525 ui.warn(_('%s: deleted in working copy\n') % relsrc)
525 ui.warn(_('%s: deleted in working copy\n') % relsrc)
526 else:
526 else:
527 ui.warn(_('%s: cannot copy - %s\n') %
527 ui.warn(_('%s: cannot copy - %s\n') %
528 (relsrc, inst.strerror))
528 (relsrc, inst.strerror))
529 errors += 1
529 errors += 1
530 return
530 return
531 if ui.verbose or not exact:
531 if ui.verbose or not exact:
532 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
532 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
533 targets[abstarget] = abssrc
533 targets[abstarget] = abssrc
534 if abstarget != origsrc:
534 if abstarget != origsrc:
535 if repo.dirstate.state(origsrc) == 'a':
535 if repo.dirstate.state(origsrc) == 'a':
536 ui.warn(_("%s was marked for addition. "
536 ui.warn(_("%s was marked for addition. "
537 "%s will not be committed as a copy.\n")
537 "%s will not be committed as a copy.\n")
538 % (repo.pathto(origsrc, cwd), reltarget))
538 % (repo.pathto(origsrc, cwd), reltarget))
539 if abstarget not in repo.dirstate and not opts.get('dry_run'):
539 if abstarget not in repo.dirstate and not opts.get('dry_run'):
540 repo.add([abstarget], wlock)
540 repo.add([abstarget], wlock)
541 elif not opts.get('dry_run'):
541 elif not opts.get('dry_run'):
542 repo.copy(origsrc, abstarget, wlock)
542 repo.copy(origsrc, abstarget, wlock)
543 copied.append((abssrc, relsrc, exact))
543 copied.append((abssrc, relsrc, exact))
544
544
545 # pat: ossep
545 # pat: ossep
546 # dest ossep
546 # dest ossep
547 # srcs: list of (hgsep, hgsep, ossep, bool)
547 # srcs: list of (hgsep, hgsep, ossep, bool)
548 # return: function that takes hgsep and returns ossep
548 # return: function that takes hgsep and returns ossep
549 def targetpathfn(pat, dest, srcs):
549 def targetpathfn(pat, dest, srcs):
550 if os.path.isdir(pat):
550 if os.path.isdir(pat):
551 abspfx = util.canonpath(repo.root, cwd, pat)
551 abspfx = util.canonpath(repo.root, cwd, pat)
552 abspfx = util.localpath(abspfx)
552 abspfx = util.localpath(abspfx)
553 if destdirexists:
553 if destdirexists:
554 striplen = len(os.path.split(abspfx)[0])
554 striplen = len(os.path.split(abspfx)[0])
555 else:
555 else:
556 striplen = len(abspfx)
556 striplen = len(abspfx)
557 if striplen:
557 if striplen:
558 striplen += len(os.sep)
558 striplen += len(os.sep)
559 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
559 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
560 elif destdirexists:
560 elif destdirexists:
561 res = lambda p: os.path.join(dest,
561 res = lambda p: os.path.join(dest,
562 os.path.basename(util.localpath(p)))
562 os.path.basename(util.localpath(p)))
563 else:
563 else:
564 res = lambda p: dest
564 res = lambda p: dest
565 return res
565 return res
566
566
567 # pat: ossep
567 # pat: ossep
568 # dest ossep
568 # dest ossep
569 # srcs: list of (hgsep, hgsep, ossep, bool)
569 # srcs: list of (hgsep, hgsep, ossep, bool)
570 # return: function that takes hgsep and returns ossep
570 # return: function that takes hgsep and returns ossep
571 def targetpathafterfn(pat, dest, srcs):
571 def targetpathafterfn(pat, dest, srcs):
572 if util.patkind(pat, None)[0]:
572 if util.patkind(pat, None)[0]:
573 # a mercurial pattern
573 # a mercurial pattern
574 res = lambda p: os.path.join(dest,
574 res = lambda p: os.path.join(dest,
575 os.path.basename(util.localpath(p)))
575 os.path.basename(util.localpath(p)))
576 else:
576 else:
577 abspfx = util.canonpath(repo.root, cwd, pat)
577 abspfx = util.canonpath(repo.root, cwd, pat)
578 if len(abspfx) < len(srcs[0][0]):
578 if len(abspfx) < len(srcs[0][0]):
579 # A directory. Either the target path contains the last
579 # A directory. Either the target path contains the last
580 # component of the source path or it does not.
580 # component of the source path or it does not.
581 def evalpath(striplen):
581 def evalpath(striplen):
582 score = 0
582 score = 0
583 for s in srcs:
583 for s in srcs:
584 t = os.path.join(dest, util.localpath(s[0])[striplen:])
584 t = os.path.join(dest, util.localpath(s[0])[striplen:])
585 if os.path.exists(t):
585 if os.path.exists(t):
586 score += 1
586 score += 1
587 return score
587 return score
588
588
589 abspfx = util.localpath(abspfx)
589 abspfx = util.localpath(abspfx)
590 striplen = len(abspfx)
590 striplen = len(abspfx)
591 if striplen:
591 if striplen:
592 striplen += len(os.sep)
592 striplen += len(os.sep)
593 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
593 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
594 score = evalpath(striplen)
594 score = evalpath(striplen)
595 striplen1 = len(os.path.split(abspfx)[0])
595 striplen1 = len(os.path.split(abspfx)[0])
596 if striplen1:
596 if striplen1:
597 striplen1 += len(os.sep)
597 striplen1 += len(os.sep)
598 if evalpath(striplen1) > score:
598 if evalpath(striplen1) > score:
599 striplen = striplen1
599 striplen = striplen1
600 res = lambda p: os.path.join(dest,
600 res = lambda p: os.path.join(dest,
601 util.localpath(p)[striplen:])
601 util.localpath(p)[striplen:])
602 else:
602 else:
603 # a file
603 # a file
604 if destdirexists:
604 if destdirexists:
605 res = lambda p: os.path.join(dest,
605 res = lambda p: os.path.join(dest,
606 os.path.basename(util.localpath(p)))
606 os.path.basename(util.localpath(p)))
607 else:
607 else:
608 res = lambda p: dest
608 res = lambda p: dest
609 return res
609 return res
610
610
611
611
612 pats = util.expand_glob(pats)
612 pats = util.expand_glob(pats)
613 if not pats:
613 if not pats:
614 raise util.Abort(_('no source or destination specified'))
614 raise util.Abort(_('no source or destination specified'))
615 if len(pats) == 1:
615 if len(pats) == 1:
616 raise util.Abort(_('no destination specified'))
616 raise util.Abort(_('no destination specified'))
617 dest = pats.pop()
617 dest = pats.pop()
618 destdirexists = os.path.isdir(dest)
618 destdirexists = os.path.isdir(dest)
619 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
619 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
620 raise util.Abort(_('with multiple sources, destination must be an '
620 raise util.Abort(_('with multiple sources, destination must be an '
621 'existing directory'))
621 'existing directory'))
622 if opts['after']:
622 if opts['after']:
623 tfn = targetpathafterfn
623 tfn = targetpathafterfn
624 else:
624 else:
625 tfn = targetpathfn
625 tfn = targetpathfn
626 copylist = []
626 copylist = []
627 for pat in pats:
627 for pat in pats:
628 srcs = []
628 srcs = []
629 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
629 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
630 globbed=True):
630 globbed=True):
631 origsrc = okaytocopy(abssrc, relsrc, exact)
631 origsrc = okaytocopy(abssrc, relsrc, exact)
632 if origsrc:
632 if origsrc:
633 srcs.append((origsrc, abssrc, relsrc, exact))
633 srcs.append((origsrc, abssrc, relsrc, exact))
634 if not srcs:
634 if not srcs:
635 continue
635 continue
636 copylist.append((tfn(pat, dest, srcs), srcs))
636 copylist.append((tfn(pat, dest, srcs), srcs))
637 if not copylist:
637 if not copylist:
638 raise util.Abort(_('no files to copy'))
638 raise util.Abort(_('no files to copy'))
639
639
640 for targetpath, srcs in copylist:
640 for targetpath, srcs in copylist:
641 for origsrc, abssrc, relsrc, exact in srcs:
641 for origsrc, abssrc, relsrc, exact in srcs:
642 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
642 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
643
643
644 if errors:
644 if errors:
645 ui.warn(_('(consider using --after)\n'))
645 ui.warn(_('(consider using --after)\n'))
646 return errors, copied
646 return errors, copied
647
647
648 def copy(ui, repo, *pats, **opts):
648 def copy(ui, repo, *pats, **opts):
649 """mark files as copied for the next commit
649 """mark files as copied for the next commit
650
650
651 Mark dest as having copies of source files. If dest is a
651 Mark dest as having copies of source files. If dest is a
652 directory, copies are put in that directory. If dest is a file,
652 directory, copies are put in that directory. If dest is a file,
653 there can only be one source.
653 there can only be one source.
654
654
655 By default, this command copies the contents of files as they
655 By default, this command copies the contents of files as they
656 stand in the working directory. If invoked with --after, the
656 stand in the working directory. If invoked with --after, the
657 operation is recorded, but no copying is performed.
657 operation is recorded, but no copying is performed.
658
658
659 This command takes effect in the next commit. To undo a copy
659 This command takes effect in the next commit. To undo a copy
660 before that, see hg revert.
660 before that, see hg revert.
661 """
661 """
662 wlock = repo.wlock(0)
662 wlock = repo.wlock(0)
663 errs, copied = docopy(ui, repo, pats, opts, wlock)
663 errs, copied = docopy(ui, repo, pats, opts, wlock)
664 return errs
664 return errs
665
665
666 def debugancestor(ui, index, rev1, rev2):
666 def debugancestor(ui, index, rev1, rev2):
667 """find the ancestor revision of two revisions in a given index"""
667 """find the ancestor revision of two revisions in a given index"""
668 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
668 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
669 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
669 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
670 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
670 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
671
671
672 def debugcomplete(ui, cmd='', **opts):
672 def debugcomplete(ui, cmd='', **opts):
673 """returns the completion list associated with the given command"""
673 """returns the completion list associated with the given command"""
674
674
675 if opts['options']:
675 if opts['options']:
676 options = []
676 options = []
677 otables = [globalopts]
677 otables = [globalopts]
678 if cmd:
678 if cmd:
679 aliases, entry = cmdutil.findcmd(ui, cmd)
679 aliases, entry = cmdutil.findcmd(ui, cmd)
680 otables.append(entry[1])
680 otables.append(entry[1])
681 for t in otables:
681 for t in otables:
682 for o in t:
682 for o in t:
683 if o[0]:
683 if o[0]:
684 options.append('-%s' % o[0])
684 options.append('-%s' % o[0])
685 options.append('--%s' % o[1])
685 options.append('--%s' % o[1])
686 ui.write("%s\n" % "\n".join(options))
686 ui.write("%s\n" % "\n".join(options))
687 return
687 return
688
688
689 clist = cmdutil.findpossible(ui, cmd).keys()
689 clist = cmdutil.findpossible(ui, cmd).keys()
690 clist.sort()
690 clist.sort()
691 ui.write("%s\n" % "\n".join(clist))
691 ui.write("%s\n" % "\n".join(clist))
692
692
693 def debugrebuildstate(ui, repo, rev=""):
693 def debugrebuildstate(ui, repo, rev=""):
694 """rebuild the dirstate as it would look like for the given revision"""
694 """rebuild the dirstate as it would look like for the given revision"""
695 if rev == "":
695 if rev == "":
696 rev = repo.changelog.tip()
696 rev = repo.changelog.tip()
697 ctx = repo.changectx(rev)
697 ctx = repo.changectx(rev)
698 files = ctx.manifest()
698 files = ctx.manifest()
699 wlock = repo.wlock()
699 wlock = repo.wlock()
700 repo.dirstate.rebuild(rev, files)
700 repo.dirstate.rebuild(rev, files)
701
701
702 def debugcheckstate(ui, repo):
702 def debugcheckstate(ui, repo):
703 """validate the correctness of the current dirstate"""
703 """validate the correctness of the current dirstate"""
704 parent1, parent2 = repo.dirstate.parents()
704 parent1, parent2 = repo.dirstate.parents()
705 dc = repo.dirstate
705 dc = repo.dirstate
706 m1 = repo.changectx(parent1).manifest()
706 m1 = repo.changectx(parent1).manifest()
707 m2 = repo.changectx(parent2).manifest()
707 m2 = repo.changectx(parent2).manifest()
708 errors = 0
708 errors = 0
709 for f in dc:
709 for f in dc:
710 state = repo.dirstate.state(f)
710 state = repo.dirstate.state(f)
711 if state in "nr" and f not in m1:
711 if state in "nr" and f not in m1:
712 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
712 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
713 errors += 1
713 errors += 1
714 if state in "a" and f in m1:
714 if state in "a" and f in m1:
715 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
715 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
716 errors += 1
716 errors += 1
717 if state in "m" and f not in m1 and f not in m2:
717 if state in "m" and f not in m1 and f not in m2:
718 ui.warn(_("%s in state %s, but not in either manifest\n") %
718 ui.warn(_("%s in state %s, but not in either manifest\n") %
719 (f, state))
719 (f, state))
720 errors += 1
720 errors += 1
721 for f in m1:
721 for f in m1:
722 state = repo.dirstate.state(f)
722 state = repo.dirstate.state(f)
723 if state not in "nrm":
723 if state not in "nrm":
724 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
724 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
725 errors += 1
725 errors += 1
726 if errors:
726 if errors:
727 error = _(".hg/dirstate inconsistent with current parent's manifest")
727 error = _(".hg/dirstate inconsistent with current parent's manifest")
728 raise util.Abort(error)
728 raise util.Abort(error)
729
729
730 def showconfig(ui, repo, *values, **opts):
730 def showconfig(ui, repo, *values, **opts):
731 """show combined config settings from all hgrc files
731 """show combined config settings from all hgrc files
732
732
733 With no args, print names and values of all config items.
733 With no args, print names and values of all config items.
734
734
735 With one arg of the form section.name, print just the value of
735 With one arg of the form section.name, print just the value of
736 that config item.
736 that config item.
737
737
738 With multiple args, print names and values of all config items
738 With multiple args, print names and values of all config items
739 with matching section names."""
739 with matching section names."""
740
740
741 untrusted = bool(opts.get('untrusted'))
741 untrusted = bool(opts.get('untrusted'))
742 if values:
742 if values:
743 if len([v for v in values if '.' in v]) > 1:
743 if len([v for v in values if '.' in v]) > 1:
744 raise util.Abort(_('only one config item permitted'))
744 raise util.Abort(_('only one config item permitted'))
745 for section, name, value in ui.walkconfig(untrusted=untrusted):
745 for section, name, value in ui.walkconfig(untrusted=untrusted):
746 sectname = section + '.' + name
746 sectname = section + '.' + name
747 if values:
747 if values:
748 for v in values:
748 for v in values:
749 if v == section:
749 if v == section:
750 ui.write('%s=%s\n' % (sectname, value))
750 ui.write('%s=%s\n' % (sectname, value))
751 elif v == sectname:
751 elif v == sectname:
752 ui.write(value, '\n')
752 ui.write(value, '\n')
753 else:
753 else:
754 ui.write('%s=%s\n' % (sectname, value))
754 ui.write('%s=%s\n' % (sectname, value))
755
755
756 def debugsetparents(ui, repo, rev1, rev2=None):
756 def debugsetparents(ui, repo, rev1, rev2=None):
757 """manually set the parents of the current working directory
757 """manually set the parents of the current working directory
758
758
759 This is useful for writing repository conversion tools, but should
759 This is useful for writing repository conversion tools, but should
760 be used with care.
760 be used with care.
761 """
761 """
762
762
763 if not rev2:
763 if not rev2:
764 rev2 = hex(nullid)
764 rev2 = hex(nullid)
765
765
766 wlock = repo.wlock()
766 wlock = repo.wlock()
767 try:
767 try:
768 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
768 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
769 finally:
769 finally:
770 wlock.release()
770 wlock.release()
771
771
772 def debugstate(ui, repo):
772 def debugstate(ui, repo):
773 """show the contents of the current dirstate"""
773 """show the contents of the current dirstate"""
774 dc = repo.dirstate
774 dc = repo.dirstate
775 for file_ in dc:
775 for file_ in dc:
776 if dc[file_][3] == -1:
776 if dc[file_][3] == -1:
777 # Pad or slice to locale representation
777 # Pad or slice to locale representation
778 locale_len = len(time.strftime("%x %X", time.localtime(0)))
778 locale_len = len(time.strftime("%x %X", time.localtime(0)))
779 timestr = 'unset'
779 timestr = 'unset'
780 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
780 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
781 else:
781 else:
782 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
782 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
783 ui.write("%c %3o %10d %s %s\n"
783 ui.write("%c %3o %10d %s %s\n"
784 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
784 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
785 timestr, file_))
785 timestr, file_))
786 for f in repo.dirstate.copies():
786 for f in repo.dirstate.copies():
787 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
787 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
788
788
789 def debugdata(ui, file_, rev):
789 def debugdata(ui, file_, rev):
790 """dump the contents of a data file revision"""
790 """dump the contents of a data file revision"""
791 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
791 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
792 try:
792 try:
793 ui.write(r.revision(r.lookup(rev)))
793 ui.write(r.revision(r.lookup(rev)))
794 except KeyError:
794 except KeyError:
795 raise util.Abort(_('invalid revision identifier %s') % rev)
795 raise util.Abort(_('invalid revision identifier %s') % rev)
796
796
797 def debugdate(ui, date, range=None, **opts):
797 def debugdate(ui, date, range=None, **opts):
798 """parse and display a date"""
798 """parse and display a date"""
799 if opts["extended"]:
799 if opts["extended"]:
800 d = util.parsedate(date, util.extendeddateformats)
800 d = util.parsedate(date, util.extendeddateformats)
801 else:
801 else:
802 d = util.parsedate(date)
802 d = util.parsedate(date)
803 ui.write("internal: %s %s\n" % d)
803 ui.write("internal: %s %s\n" % d)
804 ui.write("standard: %s\n" % util.datestr(d))
804 ui.write("standard: %s\n" % util.datestr(d))
805 if range:
805 if range:
806 m = util.matchdate(range)
806 m = util.matchdate(range)
807 ui.write("match: %s\n" % m(d[0]))
807 ui.write("match: %s\n" % m(d[0]))
808
808
809 def debugindex(ui, file_):
809 def debugindex(ui, file_):
810 """dump the contents of an index file"""
810 """dump the contents of an index file"""
811 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
811 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
812 ui.write(" rev offset length base linkrev" +
812 ui.write(" rev offset length base linkrev" +
813 " nodeid p1 p2\n")
813 " nodeid p1 p2\n")
814 for i in xrange(r.count()):
814 for i in xrange(r.count()):
815 node = r.node(i)
815 node = r.node(i)
816 pp = r.parents(node)
816 pp = r.parents(node)
817 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
817 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
818 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
818 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
819 short(node), short(pp[0]), short(pp[1])))
819 short(node), short(pp[0]), short(pp[1])))
820
820
821 def debugindexdot(ui, file_):
821 def debugindexdot(ui, file_):
822 """dump an index DAG as a .dot file"""
822 """dump an index DAG as a .dot file"""
823 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
823 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
824 ui.write("digraph G {\n")
824 ui.write("digraph G {\n")
825 for i in xrange(r.count()):
825 for i in xrange(r.count()):
826 node = r.node(i)
826 node = r.node(i)
827 pp = r.parents(node)
827 pp = r.parents(node)
828 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
828 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
829 if pp[1] != nullid:
829 if pp[1] != nullid:
830 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
830 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
831 ui.write("}\n")
831 ui.write("}\n")
832
832
833 def debuginstall(ui):
833 def debuginstall(ui):
834 '''test Mercurial installation'''
834 '''test Mercurial installation'''
835
835
836 def writetemp(contents):
836 def writetemp(contents):
837 (fd, name) = tempfile.mkstemp()
837 (fd, name) = tempfile.mkstemp()
838 f = os.fdopen(fd, "wb")
838 f = os.fdopen(fd, "wb")
839 f.write(contents)
839 f.write(contents)
840 f.close()
840 f.close()
841 return name
841 return name
842
842
843 problems = 0
843 problems = 0
844
844
845 # encoding
845 # encoding
846 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
846 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
847 try:
847 try:
848 util.fromlocal("test")
848 util.fromlocal("test")
849 except util.Abort, inst:
849 except util.Abort, inst:
850 ui.write(" %s\n" % inst)
850 ui.write(" %s\n" % inst)
851 ui.write(_(" (check that your locale is properly set)\n"))
851 ui.write(_(" (check that your locale is properly set)\n"))
852 problems += 1
852 problems += 1
853
853
854 # compiled modules
854 # compiled modules
855 ui.status(_("Checking extensions...\n"))
855 ui.status(_("Checking extensions...\n"))
856 try:
856 try:
857 import bdiff, mpatch, base85
857 import bdiff, mpatch, base85
858 except Exception, inst:
858 except Exception, inst:
859 ui.write(" %s\n" % inst)
859 ui.write(" %s\n" % inst)
860 ui.write(_(" One or more extensions could not be found"))
860 ui.write(_(" One or more extensions could not be found"))
861 ui.write(_(" (check that you compiled the extensions)\n"))
861 ui.write(_(" (check that you compiled the extensions)\n"))
862 problems += 1
862 problems += 1
863
863
864 # templates
864 # templates
865 ui.status(_("Checking templates...\n"))
865 ui.status(_("Checking templates...\n"))
866 try:
866 try:
867 import templater
867 import templater
868 t = templater.templater(templater.templatepath("map-cmdline.default"))
868 t = templater.templater(templater.templatepath("map-cmdline.default"))
869 except Exception, inst:
869 except Exception, inst:
870 ui.write(" %s\n" % inst)
870 ui.write(" %s\n" % inst)
871 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
871 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
872 problems += 1
872 problems += 1
873
873
874 # patch
874 # patch
875 ui.status(_("Checking patch...\n"))
875 ui.status(_("Checking patch...\n"))
876 patcher = ui.config('ui', 'patch')
876 patcher = ui.config('ui', 'patch')
877 patcher = ((patcher and util.find_exe(patcher)) or
877 patcher = ((patcher and util.find_exe(patcher)) or
878 util.find_exe('gpatch') or
878 util.find_exe('gpatch') or
879 util.find_exe('patch'))
879 util.find_exe('patch'))
880 if not patcher:
880 if not patcher:
881 ui.write(_(" Can't find patch or gpatch in PATH\n"))
881 ui.write(_(" Can't find patch or gpatch in PATH\n"))
882 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
882 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
883 problems += 1
883 problems += 1
884 else:
884 else:
885 # actually attempt a patch here
885 # actually attempt a patch here
886 a = "1\n2\n3\n4\n"
886 a = "1\n2\n3\n4\n"
887 b = "1\n2\n3\ninsert\n4\n"
887 b = "1\n2\n3\ninsert\n4\n"
888 fa = writetemp(a)
888 fa = writetemp(a)
889 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa))
889 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa))
890 fd = writetemp(d)
890 fd = writetemp(d)
891
891
892 files = {}
892 files = {}
893 try:
893 try:
894 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
894 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
895 except util.Abort, e:
895 except util.Abort, e:
896 ui.write(_(" patch call failed:\n"))
896 ui.write(_(" patch call failed:\n"))
897 ui.write(" " + str(e) + "\n")
897 ui.write(" " + str(e) + "\n")
898 problems += 1
898 problems += 1
899 else:
899 else:
900 if list(files) != [os.path.basename(fa)]:
900 if list(files) != [os.path.basename(fa)]:
901 ui.write(_(" unexpected patch output!"))
901 ui.write(_(" unexpected patch output!"))
902 ui.write(_(" (you may have an incompatible version of patch)\n"))
902 ui.write(_(" (you may have an incompatible version of patch)\n"))
903 problems += 1
903 problems += 1
904 a = file(fa).read()
904 a = file(fa).read()
905 if a != b:
905 if a != b:
906 ui.write(_(" patch test failed!"))
906 ui.write(_(" patch test failed!"))
907 ui.write(_(" (you may have an incompatible version of patch)\n"))
907 ui.write(_(" (you may have an incompatible version of patch)\n"))
908 problems += 1
908 problems += 1
909
909
910 os.unlink(fa)
910 os.unlink(fa)
911 os.unlink(fd)
911 os.unlink(fd)
912
912
913 # merge helper
913 # merge helper
914 ui.status(_("Checking merge helper...\n"))
914 ui.status(_("Checking merge helper...\n"))
915 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
915 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
916 or "hgmerge")
916 or "hgmerge")
917 cmdpath = util.find_exe(cmd) or util.find_exe(cmd.split()[0])
917 cmdpath = util.find_exe(cmd) or util.find_exe(cmd.split()[0])
918 if not cmdpath:
918 if not cmdpath:
919 if cmd == 'hgmerge':
919 if cmd == 'hgmerge':
920 ui.write(_(" No merge helper set and can't find default"
920 ui.write(_(" No merge helper set and can't find default"
921 " hgmerge script in PATH\n"))
921 " hgmerge script in PATH\n"))
922 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
922 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
923 else:
923 else:
924 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
924 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
925 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
925 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
926 problems += 1
926 problems += 1
927 else:
927 else:
928 # actually attempt a patch here
928 # actually attempt a patch here
929 fa = writetemp("1\n2\n3\n4\n")
929 fa = writetemp("1\n2\n3\n4\n")
930 fl = writetemp("1\n2\n3\ninsert\n4\n")
930 fl = writetemp("1\n2\n3\ninsert\n4\n")
931 fr = writetemp("begin\n1\n2\n3\n4\n")
931 fr = writetemp("begin\n1\n2\n3\n4\n")
932 r = util.system('%s "%s" "%s" "%s"' % (cmd, fl, fa, fr))
932 r = util.system('%s "%s" "%s" "%s"' % (cmd, fl, fa, fr))
933 if r:
933 if r:
934 ui.write(_(" Got unexpected merge error %d!\n") % r)
934 ui.write(_(" Got unexpected merge error %d!\n") % r)
935 problems += 1
935 problems += 1
936 m = file(fl).read()
936 m = file(fl).read()
937 if m != "begin\n1\n2\n3\ninsert\n4\n":
937 if m != "begin\n1\n2\n3\ninsert\n4\n":
938 ui.write(_(" Got unexpected merge results!\n"))
938 ui.write(_(" Got unexpected merge results!\n"))
939 ui.write(_(" (your merge helper may have the"
939 ui.write(_(" (your merge helper may have the"
940 " wrong argument order)\n"))
940 " wrong argument order)\n"))
941 ui.write(_(" Result: %r\n") % m)
941 ui.write(_(" Result: %r\n") % m)
942 problems += 1
942 problems += 1
943 os.unlink(fa)
943 os.unlink(fa)
944 os.unlink(fl)
944 os.unlink(fl)
945 os.unlink(fr)
945 os.unlink(fr)
946
946
947 # editor
947 # editor
948 ui.status(_("Checking commit editor...\n"))
948 ui.status(_("Checking commit editor...\n"))
949 editor = (os.environ.get("HGEDITOR") or
949 editor = (os.environ.get("HGEDITOR") or
950 ui.config("ui", "editor") or
950 ui.config("ui", "editor") or
951 os.environ.get("EDITOR", "vi"))
951 os.environ.get("EDITOR", "vi"))
952 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
952 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
953 if not cmdpath:
953 if not cmdpath:
954 if editor == 'vi':
954 if editor == 'vi':
955 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
955 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
956 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
956 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
957 else:
957 else:
958 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
958 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
959 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
959 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
960 problems += 1
960 problems += 1
961
961
962 # check username
962 # check username
963 ui.status(_("Checking username...\n"))
963 ui.status(_("Checking username...\n"))
964 user = os.environ.get("HGUSER")
964 user = os.environ.get("HGUSER")
965 if user is None:
965 if user is None:
966 user = ui.config("ui", "username")
966 user = ui.config("ui", "username")
967 if user is None:
967 if user is None:
968 user = os.environ.get("EMAIL")
968 user = os.environ.get("EMAIL")
969 if not user:
969 if not user:
970 ui.warn(" ")
970 ui.warn(" ")
971 ui.username()
971 ui.username()
972 ui.write(_(" (specify a username in your .hgrc file)\n"))
972 ui.write(_(" (specify a username in your .hgrc file)\n"))
973
973
974 if not problems:
974 if not problems:
975 ui.status(_("No problems detected\n"))
975 ui.status(_("No problems detected\n"))
976 else:
976 else:
977 ui.write(_("%s problems detected,"
977 ui.write(_("%s problems detected,"
978 " please check your install!\n") % problems)
978 " please check your install!\n") % problems)
979
979
980 return problems
980 return problems
981
981
982 def debugrename(ui, repo, file1, *pats, **opts):
982 def debugrename(ui, repo, file1, *pats, **opts):
983 """dump rename information"""
983 """dump rename information"""
984
984
985 ctx = repo.changectx(opts.get('rev', 'tip'))
985 ctx = repo.changectx(opts.get('rev', 'tip'))
986 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
986 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
987 ctx.node()):
987 ctx.node()):
988 m = ctx.filectx(abs).renamed()
988 m = ctx.filectx(abs).renamed()
989 if m:
989 if m:
990 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
990 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
991 else:
991 else:
992 ui.write(_("%s not renamed\n") % rel)
992 ui.write(_("%s not renamed\n") % rel)
993
993
994 def debugwalk(ui, repo, *pats, **opts):
994 def debugwalk(ui, repo, *pats, **opts):
995 """show how files match on given patterns"""
995 """show how files match on given patterns"""
996 items = list(cmdutil.walk(repo, pats, opts))
996 items = list(cmdutil.walk(repo, pats, opts))
997 if not items:
997 if not items:
998 return
998 return
999 fmt = '%%s %%-%ds %%-%ds %%s' % (
999 fmt = '%%s %%-%ds %%-%ds %%s' % (
1000 max([len(abs) for (src, abs, rel, exact) in items]),
1000 max([len(abs) for (src, abs, rel, exact) in items]),
1001 max([len(rel) for (src, abs, rel, exact) in items]))
1001 max([len(rel) for (src, abs, rel, exact) in items]))
1002 for src, abs, rel, exact in items:
1002 for src, abs, rel, exact in items:
1003 line = fmt % (src, abs, rel, exact and 'exact' or '')
1003 line = fmt % (src, abs, rel, exact and 'exact' or '')
1004 ui.write("%s\n" % line.rstrip())
1004 ui.write("%s\n" % line.rstrip())
1005
1005
1006 def diff(ui, repo, *pats, **opts):
1006 def diff(ui, repo, *pats, **opts):
1007 """diff repository (or selected files)
1007 """diff repository (or selected files)
1008
1008
1009 Show differences between revisions for the specified files.
1009 Show differences between revisions for the specified files.
1010
1010
1011 Differences between files are shown using the unified diff format.
1011 Differences between files are shown using the unified diff format.
1012
1012
1013 NOTE: diff may generate unexpected results for merges, as it will
1013 NOTE: diff may generate unexpected results for merges, as it will
1014 default to comparing against the working directory's first parent
1014 default to comparing against the working directory's first parent
1015 changeset if no revisions are specified.
1015 changeset if no revisions are specified.
1016
1016
1017 When two revision arguments are given, then changes are shown
1017 When two revision arguments are given, then changes are shown
1018 between those revisions. If only one revision is specified then
1018 between those revisions. If only one revision is specified then
1019 that revision is compared to the working directory, and, when no
1019 that revision is compared to the working directory, and, when no
1020 revisions are specified, the working directory files are compared
1020 revisions are specified, the working directory files are compared
1021 to its parent.
1021 to its parent.
1022
1022
1023 Without the -a option, diff will avoid generating diffs of files
1023 Without the -a option, diff will avoid generating diffs of files
1024 it detects as binary. With -a, diff will generate a diff anyway,
1024 it detects as binary. With -a, diff will generate a diff anyway,
1025 probably with undesirable results.
1025 probably with undesirable results.
1026 """
1026 """
1027 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1027 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1028
1028
1029 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1029 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1030
1030
1031 patch.diff(repo, node1, node2, fns, match=matchfn,
1031 patch.diff(repo, node1, node2, fns, match=matchfn,
1032 opts=patch.diffopts(ui, opts))
1032 opts=patch.diffopts(ui, opts))
1033
1033
1034 def export(ui, repo, *changesets, **opts):
1034 def export(ui, repo, *changesets, **opts):
1035 """dump the header and diffs for one or more changesets
1035 """dump the header and diffs for one or more changesets
1036
1036
1037 Print the changeset header and diffs for one or more revisions.
1037 Print the changeset header and diffs for one or more revisions.
1038
1038
1039 The information shown in the changeset header is: author,
1039 The information shown in the changeset header is: author,
1040 changeset hash, parent(s) and commit comment.
1040 changeset hash, parent(s) and commit comment.
1041
1041
1042 NOTE: export may generate unexpected diff output for merge changesets,
1042 NOTE: export may generate unexpected diff output for merge changesets,
1043 as it will compare the merge changeset against its first parent only.
1043 as it will compare the merge changeset against its first parent only.
1044
1044
1045 Output may be to a file, in which case the name of the file is
1045 Output may be to a file, in which case the name of the file is
1046 given using a format string. The formatting rules are as follows:
1046 given using a format string. The formatting rules are as follows:
1047
1047
1048 %% literal "%" character
1048 %% literal "%" character
1049 %H changeset hash (40 bytes of hexadecimal)
1049 %H changeset hash (40 bytes of hexadecimal)
1050 %N number of patches being generated
1050 %N number of patches being generated
1051 %R changeset revision number
1051 %R changeset revision number
1052 %b basename of the exporting repository
1052 %b basename of the exporting repository
1053 %h short-form changeset hash (12 bytes of hexadecimal)
1053 %h short-form changeset hash (12 bytes of hexadecimal)
1054 %n zero-padded sequence number, starting at 1
1054 %n zero-padded sequence number, starting at 1
1055 %r zero-padded changeset revision number
1055 %r zero-padded changeset revision number
1056
1056
1057 Without the -a option, export will avoid generating diffs of files
1057 Without the -a option, export will avoid generating diffs of files
1058 it detects as binary. With -a, export will generate a diff anyway,
1058 it detects as binary. With -a, export will generate a diff anyway,
1059 probably with undesirable results.
1059 probably with undesirable results.
1060
1060
1061 With the --switch-parent option, the diff will be against the second
1061 With the --switch-parent option, the diff will be against the second
1062 parent. It can be useful to review a merge.
1062 parent. It can be useful to review a merge.
1063 """
1063 """
1064 if not changesets:
1064 if not changesets:
1065 raise util.Abort(_("export requires at least one changeset"))
1065 raise util.Abort(_("export requires at least one changeset"))
1066 revs = cmdutil.revrange(repo, changesets)
1066 revs = cmdutil.revrange(repo, changesets)
1067 if len(revs) > 1:
1067 if len(revs) > 1:
1068 ui.note(_('exporting patches:\n'))
1068 ui.note(_('exporting patches:\n'))
1069 else:
1069 else:
1070 ui.note(_('exporting patch:\n'))
1070 ui.note(_('exporting patch:\n'))
1071 patch.export(repo, revs, template=opts['output'],
1071 patch.export(repo, revs, template=opts['output'],
1072 switch_parent=opts['switch_parent'],
1072 switch_parent=opts['switch_parent'],
1073 opts=patch.diffopts(ui, opts))
1073 opts=patch.diffopts(ui, opts))
1074
1074
1075 def grep(ui, repo, pattern, *pats, **opts):
1075 def grep(ui, repo, pattern, *pats, **opts):
1076 """search for a pattern in specified files and revisions
1076 """search for a pattern in specified files and revisions
1077
1077
1078 Search revisions of files for a regular expression.
1078 Search revisions of files for a regular expression.
1079
1079
1080 This command behaves differently than Unix grep. It only accepts
1080 This command behaves differently than Unix grep. It only accepts
1081 Python/Perl regexps. It searches repository history, not the
1081 Python/Perl regexps. It searches repository history, not the
1082 working directory. It always prints the revision number in which
1082 working directory. It always prints the revision number in which
1083 a match appears.
1083 a match appears.
1084
1084
1085 By default, grep only prints output for the first revision of a
1085 By default, grep only prints output for the first revision of a
1086 file in which it finds a match. To get it to print every revision
1086 file in which it finds a match. To get it to print every revision
1087 that contains a change in match status ("-" for a match that
1087 that contains a change in match status ("-" for a match that
1088 becomes a non-match, or "+" for a non-match that becomes a match),
1088 becomes a non-match, or "+" for a non-match that becomes a match),
1089 use the --all flag.
1089 use the --all flag.
1090 """
1090 """
1091 reflags = 0
1091 reflags = 0
1092 if opts['ignore_case']:
1092 if opts['ignore_case']:
1093 reflags |= re.I
1093 reflags |= re.I
1094 regexp = re.compile(pattern, reflags)
1094 regexp = re.compile(pattern, reflags)
1095 sep, eol = ':', '\n'
1095 sep, eol = ':', '\n'
1096 if opts['print0']:
1096 if opts['print0']:
1097 sep = eol = '\0'
1097 sep = eol = '\0'
1098
1098
1099 fcache = {}
1099 fcache = {}
1100 def getfile(fn):
1100 def getfile(fn):
1101 if fn not in fcache:
1101 if fn not in fcache:
1102 fcache[fn] = repo.file(fn)
1102 fcache[fn] = repo.file(fn)
1103 return fcache[fn]
1103 return fcache[fn]
1104
1104
1105 def matchlines(body):
1105 def matchlines(body):
1106 begin = 0
1106 begin = 0
1107 linenum = 0
1107 linenum = 0
1108 while True:
1108 while True:
1109 match = regexp.search(body, begin)
1109 match = regexp.search(body, begin)
1110 if not match:
1110 if not match:
1111 break
1111 break
1112 mstart, mend = match.span()
1112 mstart, mend = match.span()
1113 linenum += body.count('\n', begin, mstart) + 1
1113 linenum += body.count('\n', begin, mstart) + 1
1114 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1114 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1115 lend = body.find('\n', mend)
1115 lend = body.find('\n', mend)
1116 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1116 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1117 begin = lend + 1
1117 begin = lend + 1
1118
1118
1119 class linestate(object):
1119 class linestate(object):
1120 def __init__(self, line, linenum, colstart, colend):
1120 def __init__(self, line, linenum, colstart, colend):
1121 self.line = line
1121 self.line = line
1122 self.linenum = linenum
1122 self.linenum = linenum
1123 self.colstart = colstart
1123 self.colstart = colstart
1124 self.colend = colend
1124 self.colend = colend
1125
1125
1126 def __eq__(self, other):
1126 def __eq__(self, other):
1127 return self.line == other.line
1127 return self.line == other.line
1128
1128
1129 matches = {}
1129 matches = {}
1130 copies = {}
1130 copies = {}
1131 def grepbody(fn, rev, body):
1131 def grepbody(fn, rev, body):
1132 matches[rev].setdefault(fn, [])
1132 matches[rev].setdefault(fn, [])
1133 m = matches[rev][fn]
1133 m = matches[rev][fn]
1134 for lnum, cstart, cend, line in matchlines(body):
1134 for lnum, cstart, cend, line in matchlines(body):
1135 s = linestate(line, lnum, cstart, cend)
1135 s = linestate(line, lnum, cstart, cend)
1136 m.append(s)
1136 m.append(s)
1137
1137
1138 def difflinestates(a, b):
1138 def difflinestates(a, b):
1139 sm = difflib.SequenceMatcher(None, a, b)
1139 sm = difflib.SequenceMatcher(None, a, b)
1140 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1140 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1141 if tag == 'insert':
1141 if tag == 'insert':
1142 for i in xrange(blo, bhi):
1142 for i in xrange(blo, bhi):
1143 yield ('+', b[i])
1143 yield ('+', b[i])
1144 elif tag == 'delete':
1144 elif tag == 'delete':
1145 for i in xrange(alo, ahi):
1145 for i in xrange(alo, ahi):
1146 yield ('-', a[i])
1146 yield ('-', a[i])
1147 elif tag == 'replace':
1147 elif tag == 'replace':
1148 for i in xrange(alo, ahi):
1148 for i in xrange(alo, ahi):
1149 yield ('-', a[i])
1149 yield ('-', a[i])
1150 for i in xrange(blo, bhi):
1150 for i in xrange(blo, bhi):
1151 yield ('+', b[i])
1151 yield ('+', b[i])
1152
1152
1153 prev = {}
1153 prev = {}
1154 def display(fn, rev, states, prevstates):
1154 def display(fn, rev, states, prevstates):
1155 found = False
1155 found = False
1156 filerevmatches = {}
1156 filerevmatches = {}
1157 r = prev.get(fn, -1)
1157 r = prev.get(fn, -1)
1158 if opts['all']:
1158 if opts['all']:
1159 iter = difflinestates(states, prevstates)
1159 iter = difflinestates(states, prevstates)
1160 else:
1160 else:
1161 iter = [('', l) for l in prevstates]
1161 iter = [('', l) for l in prevstates]
1162 for change, l in iter:
1162 for change, l in iter:
1163 cols = [fn, str(r)]
1163 cols = [fn, str(r)]
1164 if opts['line_number']:
1164 if opts['line_number']:
1165 cols.append(str(l.linenum))
1165 cols.append(str(l.linenum))
1166 if opts['all']:
1166 if opts['all']:
1167 cols.append(change)
1167 cols.append(change)
1168 if opts['user']:
1168 if opts['user']:
1169 cols.append(ui.shortuser(get(r)[1]))
1169 cols.append(ui.shortuser(get(r)[1]))
1170 if opts['files_with_matches']:
1170 if opts['files_with_matches']:
1171 c = (fn, r)
1171 c = (fn, r)
1172 if c in filerevmatches:
1172 if c in filerevmatches:
1173 continue
1173 continue
1174 filerevmatches[c] = 1
1174 filerevmatches[c] = 1
1175 else:
1175 else:
1176 cols.append(l.line)
1176 cols.append(l.line)
1177 ui.write(sep.join(cols), eol)
1177 ui.write(sep.join(cols), eol)
1178 found = True
1178 found = True
1179 return found
1179 return found
1180
1180
1181 fstate = {}
1181 fstate = {}
1182 skip = {}
1182 skip = {}
1183 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1183 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1184 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1184 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1185 found = False
1185 found = False
1186 follow = opts.get('follow')
1186 follow = opts.get('follow')
1187 for st, rev, fns in changeiter:
1187 for st, rev, fns in changeiter:
1188 if st == 'window':
1188 if st == 'window':
1189 matches.clear()
1189 matches.clear()
1190 elif st == 'add':
1190 elif st == 'add':
1191 mf = repo.changectx(rev).manifest()
1191 mf = repo.changectx(rev).manifest()
1192 matches[rev] = {}
1192 matches[rev] = {}
1193 for fn in fns:
1193 for fn in fns:
1194 if fn in skip:
1194 if fn in skip:
1195 continue
1195 continue
1196 fstate.setdefault(fn, {})
1196 fstate.setdefault(fn, {})
1197 try:
1197 try:
1198 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1198 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1199 if follow:
1199 if follow:
1200 copied = getfile(fn).renamed(mf[fn])
1200 copied = getfile(fn).renamed(mf[fn])
1201 if copied:
1201 if copied:
1202 copies.setdefault(rev, {})[fn] = copied[0]
1202 copies.setdefault(rev, {})[fn] = copied[0]
1203 except KeyError:
1203 except KeyError:
1204 pass
1204 pass
1205 elif st == 'iter':
1205 elif st == 'iter':
1206 states = matches[rev].items()
1206 states = matches[rev].items()
1207 states.sort()
1207 states.sort()
1208 for fn, m in states:
1208 for fn, m in states:
1209 copy = copies.get(rev, {}).get(fn)
1209 copy = copies.get(rev, {}).get(fn)
1210 if fn in skip:
1210 if fn in skip:
1211 if copy:
1211 if copy:
1212 skip[copy] = True
1212 skip[copy] = True
1213 continue
1213 continue
1214 if fn in prev or fstate[fn]:
1214 if fn in prev or fstate[fn]:
1215 r = display(fn, rev, m, fstate[fn])
1215 r = display(fn, rev, m, fstate[fn])
1216 found = found or r
1216 found = found or r
1217 if r and not opts['all']:
1217 if r and not opts['all']:
1218 skip[fn] = True
1218 skip[fn] = True
1219 if copy:
1219 if copy:
1220 skip[copy] = True
1220 skip[copy] = True
1221 fstate[fn] = m
1221 fstate[fn] = m
1222 if copy:
1222 if copy:
1223 fstate[copy] = m
1223 fstate[copy] = m
1224 prev[fn] = rev
1224 prev[fn] = rev
1225
1225
1226 fstate = fstate.items()
1226 fstate = fstate.items()
1227 fstate.sort()
1227 fstate.sort()
1228 for fn, state in fstate:
1228 for fn, state in fstate:
1229 if fn in skip:
1229 if fn in skip:
1230 continue
1230 continue
1231 if fn not in copies.get(prev[fn], {}):
1231 if fn not in copies.get(prev[fn], {}):
1232 found = display(fn, rev, {}, state) or found
1232 found = display(fn, rev, {}, state) or found
1233 return (not found and 1) or 0
1233 return (not found and 1) or 0
1234
1234
1235 def heads(ui, repo, *branchrevs, **opts):
1235 def heads(ui, repo, *branchrevs, **opts):
1236 """show current repository heads or show branch heads
1236 """show current repository heads or show branch heads
1237
1237
1238 With no arguments, show all repository head changesets.
1238 With no arguments, show all repository head changesets.
1239
1239
1240 If branch or revisions names are given this will show the heads of
1240 If branch or revisions names are given this will show the heads of
1241 the specified branches or the branches those revisions are tagged
1241 the specified branches or the branches those revisions are tagged
1242 with.
1242 with.
1243
1243
1244 Repository "heads" are changesets that don't have child
1244 Repository "heads" are changesets that don't have child
1245 changesets. They are where development generally takes place and
1245 changesets. They are where development generally takes place and
1246 are the usual targets for update and merge operations.
1246 are the usual targets for update and merge operations.
1247
1247
1248 Branch heads are changesets that have a given branch tag, but have
1248 Branch heads are changesets that have a given branch tag, but have
1249 no child changesets with that tag. They are usually where
1249 no child changesets with that tag. They are usually where
1250 development on the given branch takes place.
1250 development on the given branch takes place.
1251 """
1251 """
1252 if opts['rev']:
1252 if opts['rev']:
1253 start = repo.lookup(opts['rev'])
1253 start = repo.lookup(opts['rev'])
1254 else:
1254 else:
1255 start = None
1255 start = None
1256 if not branchrevs:
1256 if not branchrevs:
1257 # Assume we're looking repo-wide heads if no revs were specified.
1257 # Assume we're looking repo-wide heads if no revs were specified.
1258 heads = repo.heads(start)
1258 heads = repo.heads(start)
1259 else:
1259 else:
1260 heads = []
1260 heads = []
1261 visitedset = util.set()
1261 visitedset = util.set()
1262 for branchrev in branchrevs:
1262 for branchrev in branchrevs:
1263 branch = repo.changectx(branchrev).branch()
1263 branch = repo.changectx(branchrev).branch()
1264 if branch in visitedset:
1264 if branch in visitedset:
1265 continue
1265 continue
1266 visitedset.add(branch)
1266 visitedset.add(branch)
1267 bheads = repo.branchheads(branch, start)
1267 bheads = repo.branchheads(branch, start)
1268 if not bheads:
1268 if not bheads:
1269 if branch != branchrev:
1269 if branch != branchrev:
1270 ui.warn(_("no changes on branch %s containing %s are "
1270 ui.warn(_("no changes on branch %s containing %s are "
1271 "reachable from %s\n")
1271 "reachable from %s\n")
1272 % (branch, branchrev, opts['rev']))
1272 % (branch, branchrev, opts['rev']))
1273 else:
1273 else:
1274 ui.warn(_("no changes on branch %s are reachable from %s\n")
1274 ui.warn(_("no changes on branch %s are reachable from %s\n")
1275 % (branch, opts['rev']))
1275 % (branch, opts['rev']))
1276 heads.extend(bheads)
1276 heads.extend(bheads)
1277 if not heads:
1277 if not heads:
1278 return 1
1278 return 1
1279 displayer = cmdutil.show_changeset(ui, repo, opts)
1279 displayer = cmdutil.show_changeset(ui, repo, opts)
1280 for n in heads:
1280 for n in heads:
1281 displayer.show(changenode=n)
1281 displayer.show(changenode=n)
1282
1282
1283 def help_(ui, name=None, with_version=False):
1283 def help_(ui, name=None, with_version=False):
1284 """show help for a command, extension, or list of commands
1284 """show help for a command, extension, or list of commands
1285
1285
1286 With no arguments, print a list of commands and short help.
1286 With no arguments, print a list of commands and short help.
1287
1287
1288 Given a command name, print help for that command.
1288 Given a command name, print help for that command.
1289
1289
1290 Given an extension name, print help for that extension, and the
1290 Given an extension name, print help for that extension, and the
1291 commands it provides."""
1291 commands it provides."""
1292 option_lists = []
1292 option_lists = []
1293
1293
1294 def addglobalopts(aliases):
1294 def addglobalopts(aliases):
1295 if ui.verbose:
1295 if ui.verbose:
1296 option_lists.append((_("global options:"), globalopts))
1296 option_lists.append((_("global options:"), globalopts))
1297 if name == 'shortlist':
1297 if name == 'shortlist':
1298 option_lists.append((_('use "hg help" for the full list '
1298 option_lists.append((_('use "hg help" for the full list '
1299 'of commands'), ()))
1299 'of commands'), ()))
1300 else:
1300 else:
1301 if name == 'shortlist':
1301 if name == 'shortlist':
1302 msg = _('use "hg help" for the full list of commands '
1302 msg = _('use "hg help" for the full list of commands '
1303 'or "hg -v" for details')
1303 'or "hg -v" for details')
1304 elif aliases:
1304 elif aliases:
1305 msg = _('use "hg -v help%s" to show aliases and '
1305 msg = _('use "hg -v help%s" to show aliases and '
1306 'global options') % (name and " " + name or "")
1306 'global options') % (name and " " + name or "")
1307 else:
1307 else:
1308 msg = _('use "hg -v help %s" to show global options') % name
1308 msg = _('use "hg -v help %s" to show global options') % name
1309 option_lists.append((msg, ()))
1309 option_lists.append((msg, ()))
1310
1310
1311 def helpcmd(name):
1311 def helpcmd(name):
1312 if with_version:
1312 if with_version:
1313 version_(ui)
1313 version_(ui)
1314 ui.write('\n')
1314 ui.write('\n')
1315 aliases, i = cmdutil.findcmd(ui, name)
1315 aliases, i = cmdutil.findcmd(ui, name)
1316 # synopsis
1316 # synopsis
1317 ui.write("%s\n\n" % i[2])
1317 ui.write("%s\n\n" % i[2])
1318
1318
1319 # description
1319 # description
1320 doc = i[0].__doc__
1320 doc = i[0].__doc__
1321 if not doc:
1321 if not doc:
1322 doc = _("(No help text available)")
1322 doc = _("(No help text available)")
1323 if ui.quiet:
1323 if ui.quiet:
1324 doc = doc.splitlines(0)[0]
1324 doc = doc.splitlines(0)[0]
1325 ui.write("%s\n" % doc.rstrip())
1325 ui.write("%s\n" % doc.rstrip())
1326
1326
1327 if not ui.quiet:
1327 if not ui.quiet:
1328 # aliases
1328 # aliases
1329 if len(aliases) > 1:
1329 if len(aliases) > 1:
1330 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1330 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1331
1331
1332 # options
1332 # options
1333 if i[1]:
1333 if i[1]:
1334 option_lists.append((_("options:\n"), i[1]))
1334 option_lists.append((_("options:\n"), i[1]))
1335
1335
1336 addglobalopts(False)
1336 addglobalopts(False)
1337
1337
1338 def helplist(select=None):
1338 def helplist(select=None):
1339 h = {}
1339 h = {}
1340 cmds = {}
1340 cmds = {}
1341 for c, e in table.items():
1341 for c, e in table.items():
1342 f = c.split("|", 1)[0]
1342 f = c.split("|", 1)[0]
1343 if select and not select(f):
1343 if select and not select(f):
1344 continue
1344 continue
1345 if name == "shortlist" and not f.startswith("^"):
1345 if name == "shortlist" and not f.startswith("^"):
1346 continue
1346 continue
1347 f = f.lstrip("^")
1347 f = f.lstrip("^")
1348 if not ui.debugflag and f.startswith("debug"):
1348 if not ui.debugflag and f.startswith("debug"):
1349 continue
1349 continue
1350 doc = e[0].__doc__
1350 doc = e[0].__doc__
1351 if not doc:
1351 if not doc:
1352 doc = _("(No help text available)")
1352 doc = _("(No help text available)")
1353 h[f] = doc.splitlines(0)[0].rstrip()
1353 h[f] = doc.splitlines(0)[0].rstrip()
1354 cmds[f] = c.lstrip("^")
1354 cmds[f] = c.lstrip("^")
1355
1355
1356 fns = h.keys()
1356 fns = h.keys()
1357 fns.sort()
1357 fns.sort()
1358 m = max(map(len, fns))
1358 m = max(map(len, fns))
1359 for f in fns:
1359 for f in fns:
1360 if ui.verbose:
1360 if ui.verbose:
1361 commands = cmds[f].replace("|",", ")
1361 commands = cmds[f].replace("|",", ")
1362 ui.write(" %s:\n %s\n"%(commands, h[f]))
1362 ui.write(" %s:\n %s\n"%(commands, h[f]))
1363 else:
1363 else:
1364 ui.write(' %-*s %s\n' % (m, f, h[f]))
1364 ui.write(' %-*s %s\n' % (m, f, h[f]))
1365
1365
1366 if not ui.quiet:
1366 if not ui.quiet:
1367 addglobalopts(True)
1367 addglobalopts(True)
1368
1368
1369 def helptopic(name):
1369 def helptopic(name):
1370 v = None
1370 v = None
1371 for i in help.helptable:
1371 for i in help.helptable:
1372 l = i.split('|')
1372 l = i.split('|')
1373 if name in l:
1373 if name in l:
1374 v = i
1374 v = i
1375 header = l[-1]
1375 header = l[-1]
1376 if not v:
1376 if not v:
1377 raise cmdutil.UnknownCommand(name)
1377 raise cmdutil.UnknownCommand(name)
1378
1378
1379 # description
1379 # description
1380 doc = help.helptable[v]
1380 doc = help.helptable[v]
1381 if not doc:
1381 if not doc:
1382 doc = _("(No help text available)")
1382 doc = _("(No help text available)")
1383 if callable(doc):
1383 if callable(doc):
1384 doc = doc()
1384 doc = doc()
1385
1385
1386 ui.write("%s\n" % header)
1386 ui.write("%s\n" % header)
1387 ui.write("%s\n" % doc.rstrip())
1387 ui.write("%s\n" % doc.rstrip())
1388
1388
1389 def helpext(name):
1389 def helpext(name):
1390 try:
1390 try:
1391 mod = extensions.find(name)
1391 mod = extensions.find(name)
1392 except KeyError:
1392 except KeyError:
1393 raise cmdutil.UnknownCommand(name)
1393 raise cmdutil.UnknownCommand(name)
1394
1394
1395 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1395 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1396 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1396 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1397 for d in doc[1:]:
1397 for d in doc[1:]:
1398 ui.write(d, '\n')
1398 ui.write(d, '\n')
1399
1399
1400 ui.status('\n')
1400 ui.status('\n')
1401
1401
1402 try:
1402 try:
1403 ct = mod.cmdtable
1403 ct = mod.cmdtable
1404 except AttributeError:
1404 except AttributeError:
1405 ct = None
1405 ct = None
1406 if not ct:
1406 if not ct:
1407 ui.status(_('no commands defined\n'))
1407 ui.status(_('no commands defined\n'))
1408 return
1408 return
1409
1409
1410 ui.status(_('list of commands:\n\n'))
1410 ui.status(_('list of commands:\n\n'))
1411 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1411 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1412 helplist(modcmds.has_key)
1412 helplist(modcmds.has_key)
1413
1413
1414 if name and name != 'shortlist':
1414 if name and name != 'shortlist':
1415 i = None
1415 i = None
1416 for f in (helpcmd, helptopic, helpext):
1416 for f in (helpcmd, helptopic, helpext):
1417 try:
1417 try:
1418 f(name)
1418 f(name)
1419 i = None
1419 i = None
1420 break
1420 break
1421 except cmdutil.UnknownCommand, inst:
1421 except cmdutil.UnknownCommand, inst:
1422 i = inst
1422 i = inst
1423 if i:
1423 if i:
1424 raise i
1424 raise i
1425
1425
1426 else:
1426 else:
1427 # program name
1427 # program name
1428 if ui.verbose or with_version:
1428 if ui.verbose or with_version:
1429 version_(ui)
1429 version_(ui)
1430 else:
1430 else:
1431 ui.status(_("Mercurial Distributed SCM\n"))
1431 ui.status(_("Mercurial Distributed SCM\n"))
1432 ui.status('\n')
1432 ui.status('\n')
1433
1433
1434 # list of commands
1434 # list of commands
1435 if name == "shortlist":
1435 if name == "shortlist":
1436 ui.status(_('basic commands:\n\n'))
1436 ui.status(_('basic commands:\n\n'))
1437 else:
1437 else:
1438 ui.status(_('list of commands:\n\n'))
1438 ui.status(_('list of commands:\n\n'))
1439
1439
1440 helplist()
1440 helplist()
1441
1441
1442 # list all option lists
1442 # list all option lists
1443 opt_output = []
1443 opt_output = []
1444 for title, options in option_lists:
1444 for title, options in option_lists:
1445 opt_output.append(("\n%s" % title, None))
1445 opt_output.append(("\n%s" % title, None))
1446 for shortopt, longopt, default, desc in options:
1446 for shortopt, longopt, default, desc in options:
1447 if "DEPRECATED" in desc and not ui.verbose: continue
1447 if "DEPRECATED" in desc and not ui.verbose: continue
1448 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1448 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1449 longopt and " --%s" % longopt),
1449 longopt and " --%s" % longopt),
1450 "%s%s" % (desc,
1450 "%s%s" % (desc,
1451 default
1451 default
1452 and _(" (default: %s)") % default
1452 and _(" (default: %s)") % default
1453 or "")))
1453 or "")))
1454
1454
1455 if opt_output:
1455 if opt_output:
1456 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1456 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1457 for first, second in opt_output:
1457 for first, second in opt_output:
1458 if second:
1458 if second:
1459 ui.write(" %-*s %s\n" % (opts_len, first, second))
1459 ui.write(" %-*s %s\n" % (opts_len, first, second))
1460 else:
1460 else:
1461 ui.write("%s\n" % first)
1461 ui.write("%s\n" % first)
1462
1462
1463 def identify(ui, repo, source=None,
1463 def identify(ui, repo, source=None,
1464 rev=None, num=None, id=None, branch=None, tags=None):
1464 rev=None, num=None, id=None, branch=None, tags=None):
1465 """identify the working copy or specified revision
1465 """identify the working copy or specified revision
1466
1466
1467 With no revision, print a summary of the current state of the repo.
1467 With no revision, print a summary of the current state of the repo.
1468
1468
1469 With a path, do a lookup in another repository.
1469 With a path, do a lookup in another repository.
1470
1470
1471 This summary identifies the repository state using one or two parent
1471 This summary identifies the repository state using one or two parent
1472 hash identifiers, followed by a "+" if there are uncommitted changes
1472 hash identifiers, followed by a "+" if there are uncommitted changes
1473 in the working directory, a list of tags for this revision and a branch
1473 in the working directory, a list of tags for this revision and a branch
1474 name for non-default branches.
1474 name for non-default branches.
1475 """
1475 """
1476
1476
1477 hexfunc = ui.debugflag and hex or short
1477 hexfunc = ui.debugflag and hex or short
1478 default = not (num or id or branch or tags)
1478 default = not (num or id or branch or tags)
1479 output = []
1479 output = []
1480
1480
1481 if source:
1481 if source:
1482 source, revs = cmdutil.parseurl(ui.expandpath(source), [])
1482 source, revs = cmdutil.parseurl(ui.expandpath(source), [])
1483 srepo = hg.repository(ui, source)
1483 srepo = hg.repository(ui, source)
1484 if not rev and revs:
1484 if not rev and revs:
1485 rev = revs[0]
1485 rev = revs[0]
1486 if not rev:
1486 if not rev:
1487 rev = "tip"
1487 rev = "tip"
1488 if num or branch or tags:
1488 if num or branch or tags:
1489 raise util.Abort(
1489 raise util.Abort(
1490 "can't query remote revision number, branch, or tags")
1490 "can't query remote revision number, branch, or tags")
1491 output = [hexfunc(srepo.lookup(rev))]
1491 output = [hexfunc(srepo.lookup(rev))]
1492 elif not rev:
1492 elif not rev:
1493 ctx = repo.workingctx()
1493 ctx = repo.workingctx()
1494 parents = ctx.parents()
1494 parents = ctx.parents()
1495 changed = False
1495 changed = False
1496 if default or id or num:
1496 if default or id or num:
1497 changed = ctx.files() + ctx.deleted()
1497 changed = ctx.files() + ctx.deleted()
1498 if default or id:
1498 if default or id:
1499 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1499 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1500 (changed) and "+" or "")]
1500 (changed) and "+" or "")]
1501 if num:
1501 if num:
1502 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1502 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1503 (changed) and "+" or ""))
1503 (changed) and "+" or ""))
1504 else:
1504 else:
1505 ctx = repo.changectx(rev)
1505 ctx = repo.changectx(rev)
1506 if default or id:
1506 if default or id:
1507 output = [hexfunc(ctx.node())]
1507 output = [hexfunc(ctx.node())]
1508 if num:
1508 if num:
1509 output.append(str(ctx.rev()))
1509 output.append(str(ctx.rev()))
1510
1510
1511 if not source and default and not ui.quiet:
1511 if not source and default and not ui.quiet:
1512 b = util.tolocal(ctx.branch())
1512 b = util.tolocal(ctx.branch())
1513 if b != 'default':
1513 if b != 'default':
1514 output.append("(%s)" % b)
1514 output.append("(%s)" % b)
1515
1515
1516 # multiple tags for a single parent separated by '/'
1516 # multiple tags for a single parent separated by '/'
1517 t = "/".join(ctx.tags())
1517 t = "/".join(ctx.tags())
1518 if t:
1518 if t:
1519 output.append(t)
1519 output.append(t)
1520
1520
1521 if branch:
1521 if branch:
1522 output.append(util.tolocal(ctx.branch()))
1522 output.append(util.tolocal(ctx.branch()))
1523
1523
1524 if tags:
1524 if tags:
1525 output.extend(ctx.tags())
1525 output.extend(ctx.tags())
1526
1526
1527 ui.write("%s\n" % ' '.join(output))
1527 ui.write("%s\n" % ' '.join(output))
1528
1528
1529 def import_(ui, repo, patch1, *patches, **opts):
1529 def import_(ui, repo, patch1, *patches, **opts):
1530 """import an ordered set of patches
1530 """import an ordered set of patches
1531
1531
1532 Import a list of patches and commit them individually.
1532 Import a list of patches and commit them individually.
1533
1533
1534 If there are outstanding changes in the working directory, import
1534 If there are outstanding changes in the working directory, import
1535 will abort unless given the -f flag.
1535 will abort unless given the -f flag.
1536
1536
1537 You can import a patch straight from a mail message. Even patches
1537 You can import a patch straight from a mail message. Even patches
1538 as attachments work (body part must be type text/plain or
1538 as attachments work (body part must be type text/plain or
1539 text/x-patch to be used). From and Subject headers of email
1539 text/x-patch to be used). From and Subject headers of email
1540 message are used as default committer and commit message. All
1540 message are used as default committer and commit message. All
1541 text/plain body parts before first diff are added to commit
1541 text/plain body parts before first diff are added to commit
1542 message.
1542 message.
1543
1543
1544 If the imported patch was generated by hg export, user and description
1544 If the imported patch was generated by hg export, user and description
1545 from patch override values from message headers and body. Values
1545 from patch override values from message headers and body. Values
1546 given on command line with -m and -u override these.
1546 given on command line with -m and -u override these.
1547
1547
1548 If --exact is specified, import will set the working directory
1548 If --exact is specified, import will set the working directory
1549 to the parent of each patch before applying it, and will abort
1549 to the parent of each patch before applying it, and will abort
1550 if the resulting changeset has a different ID than the one
1550 if the resulting changeset has a different ID than the one
1551 recorded in the patch. This may happen due to character set
1551 recorded in the patch. This may happen due to character set
1552 problems or other deficiencies in the text patch format.
1552 problems or other deficiencies in the text patch format.
1553
1553
1554 To read a patch from standard input, use patch name "-".
1554 To read a patch from standard input, use patch name "-".
1555 """
1555 """
1556 patches = (patch1,) + patches
1556 patches = (patch1,) + patches
1557
1557
1558 if opts.get('exact') or not opts['force']:
1558 if opts.get('exact') or not opts['force']:
1559 cmdutil.bail_if_changed(repo)
1559 cmdutil.bail_if_changed(repo)
1560
1560
1561 d = opts["base"]
1561 d = opts["base"]
1562 strip = opts["strip"]
1562 strip = opts["strip"]
1563
1563
1564 wlock = repo.wlock()
1564 wlock = repo.wlock()
1565 lock = repo.lock()
1565 lock = repo.lock()
1566
1566
1567 for p in patches:
1567 for p in patches:
1568 pf = os.path.join(d, p)
1568 pf = os.path.join(d, p)
1569
1569
1570 if pf == '-':
1570 if pf == '-':
1571 ui.status(_("applying patch from stdin\n"))
1571 ui.status(_("applying patch from stdin\n"))
1572 tmpname, message, user, date, branch, nodeid, p1, p2 = patch.extract(ui, sys.stdin)
1572 tmpname, message, user, date, branch, nodeid, p1, p2 = patch.extract(ui, sys.stdin)
1573 else:
1573 else:
1574 ui.status(_("applying %s\n") % p)
1574 ui.status(_("applying %s\n") % p)
1575 tmpname, message, user, date, branch, nodeid, p1, p2 = patch.extract(ui, file(pf, 'rb'))
1575 tmpname, message, user, date, branch, nodeid, p1, p2 = patch.extract(ui, file(pf, 'rb'))
1576
1576
1577 if tmpname is None:
1577 if tmpname is None:
1578 raise util.Abort(_('no diffs found'))
1578 raise util.Abort(_('no diffs found'))
1579
1579
1580 try:
1580 try:
1581 cmdline_message = cmdutil.logmessage(opts)
1581 cmdline_message = cmdutil.logmessage(opts)
1582 if cmdline_message:
1582 if cmdline_message:
1583 # pickup the cmdline msg
1583 # pickup the cmdline msg
1584 message = cmdline_message
1584 message = cmdline_message
1585 elif message:
1585 elif message:
1586 # pickup the patch msg
1586 # pickup the patch msg
1587 message = message.strip()
1587 message = message.strip()
1588 else:
1588 else:
1589 # launch the editor
1589 # launch the editor
1590 message = None
1590 message = None
1591 ui.debug(_('message:\n%s\n') % message)
1591 ui.debug(_('message:\n%s\n') % message)
1592
1592
1593 wp = repo.workingctx().parents()
1593 wp = repo.workingctx().parents()
1594 if opts.get('exact'):
1594 if opts.get('exact'):
1595 if not nodeid or not p1:
1595 if not nodeid or not p1:
1596 raise util.Abort(_('not a mercurial patch'))
1596 raise util.Abort(_('not a mercurial patch'))
1597 p1 = repo.lookup(p1)
1597 p1 = repo.lookup(p1)
1598 p2 = repo.lookup(p2 or hex(nullid))
1598 p2 = repo.lookup(p2 or hex(nullid))
1599
1599
1600 if p1 != wp[0].node():
1600 if p1 != wp[0].node():
1601 hg.clean(repo, p1, wlock=wlock)
1601 hg.clean(repo, p1, wlock=wlock)
1602 repo.dirstate.setparents(p1, p2)
1602 repo.dirstate.setparents(p1, p2)
1603 elif p2:
1603 elif p2:
1604 try:
1604 try:
1605 p1 = repo.lookup(p1)
1605 p1 = repo.lookup(p1)
1606 p2 = repo.lookup(p2)
1606 p2 = repo.lookup(p2)
1607 if p1 == wp[0].node():
1607 if p1 == wp[0].node():
1608 repo.dirstate.setparents(p1, p2)
1608 repo.dirstate.setparents(p1, p2)
1609 except hg.RepoError:
1609 except hg.RepoError:
1610 pass
1610 pass
1611 if opts.get('exact') or opts.get('import_branch'):
1611 if opts.get('exact') or opts.get('import_branch'):
1612 repo.dirstate.setbranch(branch or 'default')
1612 repo.dirstate.setbranch(branch or 'default')
1613
1613
1614 files = {}
1614 files = {}
1615 try:
1615 try:
1616 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1616 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1617 files=files)
1617 files=files)
1618 finally:
1618 finally:
1619 files = patch.updatedir(ui, repo, files, wlock=wlock)
1619 files = patch.updatedir(ui, repo, files, wlock=wlock)
1620 n = repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1620 n = repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1621 if opts.get('exact'):
1621 if opts.get('exact'):
1622 if hex(n) != nodeid:
1622 if hex(n) != nodeid:
1623 repo.rollback(wlock=wlock, lock=lock)
1623 repo.rollback(wlock=wlock, lock=lock)
1624 raise util.Abort(_('patch is damaged or loses information'))
1624 raise util.Abort(_('patch is damaged or loses information'))
1625 finally:
1625 finally:
1626 os.unlink(tmpname)
1626 os.unlink(tmpname)
1627
1627
1628 def incoming(ui, repo, source="default", **opts):
1628 def incoming(ui, repo, source="default", **opts):
1629 """show new changesets found in source
1629 """show new changesets found in source
1630
1630
1631 Show new changesets found in the specified path/URL or the default
1631 Show new changesets found in the specified path/URL or the default
1632 pull location. These are the changesets that would be pulled if a pull
1632 pull location. These are the changesets that would be pulled if a pull
1633 was requested.
1633 was requested.
1634
1634
1635 For remote repository, using --bundle avoids downloading the changesets
1635 For remote repository, using --bundle avoids downloading the changesets
1636 twice if the incoming is followed by a pull.
1636 twice if the incoming is followed by a pull.
1637
1637
1638 See pull for valid source format details.
1638 See pull for valid source format details.
1639 """
1639 """
1640 source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
1640 source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
1641 cmdutil.setremoteconfig(ui, opts)
1641 cmdutil.setremoteconfig(ui, opts)
1642
1642
1643 other = hg.repository(ui, source)
1643 other = hg.repository(ui, source)
1644 ui.status(_('comparing with %s\n') % source)
1644 ui.status(_('comparing with %s\n') % source)
1645 if revs:
1645 if revs:
1646 if 'lookup' in other.capabilities:
1646 if 'lookup' in other.capabilities:
1647 revs = [other.lookup(rev) for rev in revs]
1647 revs = [other.lookup(rev) for rev in revs]
1648 else:
1648 else:
1649 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1649 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1650 raise util.Abort(error)
1650 raise util.Abort(error)
1651 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1651 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1652 if not incoming:
1652 if not incoming:
1653 try:
1653 try:
1654 os.unlink(opts["bundle"])
1654 os.unlink(opts["bundle"])
1655 except:
1655 except:
1656 pass
1656 pass
1657 ui.status(_("no changes found\n"))
1657 ui.status(_("no changes found\n"))
1658 return 1
1658 return 1
1659
1659
1660 cleanup = None
1660 cleanup = None
1661 try:
1661 try:
1662 fname = opts["bundle"]
1662 fname = opts["bundle"]
1663 if fname or not other.local():
1663 if fname or not other.local():
1664 # create a bundle (uncompressed if other repo is not local)
1664 # create a bundle (uncompressed if other repo is not local)
1665 if revs is None:
1665 if revs is None:
1666 cg = other.changegroup(incoming, "incoming")
1666 cg = other.changegroup(incoming, "incoming")
1667 else:
1667 else:
1668 if 'changegroupsubset' not in other.capabilities:
1668 if 'changegroupsubset' not in other.capabilities:
1669 raise util.Abort(_("Partial incoming cannot be done because other repository doesn't support changegroupsubset."))
1669 raise util.Abort(_("Partial incoming cannot be done because other repository doesn't support changegroupsubset."))
1670 cg = other.changegroupsubset(incoming, revs, 'incoming')
1670 cg = other.changegroupsubset(incoming, revs, 'incoming')
1671 bundletype = other.local() and "HG10BZ" or "HG10UN"
1671 bundletype = other.local() and "HG10BZ" or "HG10UN"
1672 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1672 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1673 # keep written bundle?
1673 # keep written bundle?
1674 if opts["bundle"]:
1674 if opts["bundle"]:
1675 cleanup = None
1675 cleanup = None
1676 if not other.local():
1676 if not other.local():
1677 # use the created uncompressed bundlerepo
1677 # use the created uncompressed bundlerepo
1678 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1678 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1679
1679
1680 o = other.changelog.nodesbetween(incoming, revs)[0]
1680 o = other.changelog.nodesbetween(incoming, revs)[0]
1681 if opts['newest_first']:
1681 if opts['newest_first']:
1682 o.reverse()
1682 o.reverse()
1683 displayer = cmdutil.show_changeset(ui, other, opts)
1683 displayer = cmdutil.show_changeset(ui, other, opts)
1684 for n in o:
1684 for n in o:
1685 parents = [p for p in other.changelog.parents(n) if p != nullid]
1685 parents = [p for p in other.changelog.parents(n) if p != nullid]
1686 if opts['no_merges'] and len(parents) == 2:
1686 if opts['no_merges'] and len(parents) == 2:
1687 continue
1687 continue
1688 displayer.show(changenode=n)
1688 displayer.show(changenode=n)
1689 finally:
1689 finally:
1690 if hasattr(other, 'close'):
1690 if hasattr(other, 'close'):
1691 other.close()
1691 other.close()
1692 if cleanup:
1692 if cleanup:
1693 os.unlink(cleanup)
1693 os.unlink(cleanup)
1694
1694
1695 def init(ui, dest=".", **opts):
1695 def init(ui, dest=".", **opts):
1696 """create a new repository in the given directory
1696 """create a new repository in the given directory
1697
1697
1698 Initialize a new repository in the given directory. If the given
1698 Initialize a new repository in the given directory. If the given
1699 directory does not exist, it is created.
1699 directory does not exist, it is created.
1700
1700
1701 If no directory is given, the current directory is used.
1701 If no directory is given, the current directory is used.
1702
1702
1703 It is possible to specify an ssh:// URL as the destination.
1703 It is possible to specify an ssh:// URL as the destination.
1704 Look at the help text for the pull command for important details
1704 Look at the help text for the pull command for important details
1705 about ssh:// URLs.
1705 about ssh:// URLs.
1706 """
1706 """
1707 cmdutil.setremoteconfig(ui, opts)
1707 cmdutil.setremoteconfig(ui, opts)
1708 hg.repository(ui, dest, create=1)
1708 hg.repository(ui, dest, create=1)
1709
1709
1710 def locate(ui, repo, *pats, **opts):
1710 def locate(ui, repo, *pats, **opts):
1711 """locate files matching specific patterns
1711 """locate files matching specific patterns
1712
1712
1713 Print all files under Mercurial control whose names match the
1713 Print all files under Mercurial control whose names match the
1714 given patterns.
1714 given patterns.
1715
1715
1716 This command searches the entire repository by default. To search
1716 This command searches the entire repository by default. To search
1717 just the current directory and its subdirectories, use
1717 just the current directory and its subdirectories, use
1718 "--include .".
1718 "--include .".
1719
1719
1720 If no patterns are given to match, this command prints all file
1720 If no patterns are given to match, this command prints all file
1721 names.
1721 names.
1722
1722
1723 If you want to feed the output of this command into the "xargs"
1723 If you want to feed the output of this command into the "xargs"
1724 command, use the "-0" option to both this command and "xargs".
1724 command, use the "-0" option to both this command and "xargs".
1725 This will avoid the problem of "xargs" treating single filenames
1725 This will avoid the problem of "xargs" treating single filenames
1726 that contain white space as multiple filenames.
1726 that contain white space as multiple filenames.
1727 """
1727 """
1728 end = opts['print0'] and '\0' or '\n'
1728 end = opts['print0'] and '\0' or '\n'
1729 rev = opts['rev']
1729 rev = opts['rev']
1730 if rev:
1730 if rev:
1731 node = repo.lookup(rev)
1731 node = repo.lookup(rev)
1732 else:
1732 else:
1733 node = None
1733 node = None
1734
1734
1735 ret = 1
1735 ret = 1
1736 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1736 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1737 badmatch=util.always,
1737 badmatch=util.always,
1738 default='relglob'):
1738 default='relglob'):
1739 if src == 'b':
1739 if src == 'b':
1740 continue
1740 continue
1741 if not node and repo.dirstate.state(abs) == '?':
1741 if not node and repo.dirstate.state(abs) == '?':
1742 continue
1742 continue
1743 if opts['fullpath']:
1743 if opts['fullpath']:
1744 ui.write(os.path.join(repo.root, abs), end)
1744 ui.write(os.path.join(repo.root, abs), end)
1745 else:
1745 else:
1746 ui.write(((pats and rel) or abs), end)
1746 ui.write(((pats and rel) or abs), end)
1747 ret = 0
1747 ret = 0
1748
1748
1749 return ret
1749 return ret
1750
1750
1751 def log(ui, repo, *pats, **opts):
1751 def log(ui, repo, *pats, **opts):
1752 """show revision history of entire repository or files
1752 """show revision history of entire repository or files
1753
1753
1754 Print the revision history of the specified files or the entire
1754 Print the revision history of the specified files or the entire
1755 project.
1755 project.
1756
1756
1757 File history is shown without following rename or copy history of
1757 File history is shown without following rename or copy history of
1758 files. Use -f/--follow with a file name to follow history across
1758 files. Use -f/--follow with a file name to follow history across
1759 renames and copies. --follow without a file name will only show
1759 renames and copies. --follow without a file name will only show
1760 ancestors or descendants of the starting revision. --follow-first
1760 ancestors or descendants of the starting revision. --follow-first
1761 only follows the first parent of merge revisions.
1761 only follows the first parent of merge revisions.
1762
1762
1763 If no revision range is specified, the default is tip:0 unless
1763 If no revision range is specified, the default is tip:0 unless
1764 --follow is set, in which case the working directory parent is
1764 --follow is set, in which case the working directory parent is
1765 used as the starting revision.
1765 used as the starting revision.
1766
1766
1767 By default this command outputs: changeset id and hash, tags,
1767 By default this command outputs: changeset id and hash, tags,
1768 non-trivial parents, user, date and time, and a summary for each
1768 non-trivial parents, user, date and time, and a summary for each
1769 commit. When the -v/--verbose switch is used, the list of changed
1769 commit. When the -v/--verbose switch is used, the list of changed
1770 files and full commit message is shown.
1770 files and full commit message is shown.
1771
1771
1772 NOTE: log -p may generate unexpected diff output for merge
1772 NOTE: log -p may generate unexpected diff output for merge
1773 changesets, as it will compare the merge changeset against its
1773 changesets, as it will compare the merge changeset against its
1774 first parent only. Also, the files: list will only reflect files
1774 first parent only. Also, the files: list will only reflect files
1775 that are different from BOTH parents.
1775 that are different from BOTH parents.
1776
1776
1777 """
1777 """
1778
1778
1779 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1779 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1780 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1780 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1781
1781
1782 if opts['limit']:
1782 if opts['limit']:
1783 try:
1783 try:
1784 limit = int(opts['limit'])
1784 limit = int(opts['limit'])
1785 except ValueError:
1785 except ValueError:
1786 raise util.Abort(_('limit must be a positive integer'))
1786 raise util.Abort(_('limit must be a positive integer'))
1787 if limit <= 0: raise util.Abort(_('limit must be positive'))
1787 if limit <= 0: raise util.Abort(_('limit must be positive'))
1788 else:
1788 else:
1789 limit = sys.maxint
1789 limit = sys.maxint
1790 count = 0
1790 count = 0
1791
1791
1792 if opts['copies'] and opts['rev']:
1792 if opts['copies'] and opts['rev']:
1793 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1793 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1794 else:
1794 else:
1795 endrev = repo.changelog.count()
1795 endrev = repo.changelog.count()
1796 rcache = {}
1796 rcache = {}
1797 ncache = {}
1797 ncache = {}
1798 dcache = []
1798 dcache = []
1799 def getrenamed(fn, rev, man):
1799 def getrenamed(fn, rev, man):
1800 '''looks up all renames for a file (up to endrev) the first
1800 '''looks up all renames for a file (up to endrev) the first
1801 time the file is given. It indexes on the changerev and only
1801 time the file is given. It indexes on the changerev and only
1802 parses the manifest if linkrev != changerev.
1802 parses the manifest if linkrev != changerev.
1803 Returns rename info for fn at changerev rev.'''
1803 Returns rename info for fn at changerev rev.'''
1804 if fn not in rcache:
1804 if fn not in rcache:
1805 rcache[fn] = {}
1805 rcache[fn] = {}
1806 ncache[fn] = {}
1806 ncache[fn] = {}
1807 fl = repo.file(fn)
1807 fl = repo.file(fn)
1808 for i in xrange(fl.count()):
1808 for i in xrange(fl.count()):
1809 node = fl.node(i)
1809 node = fl.node(i)
1810 lr = fl.linkrev(node)
1810 lr = fl.linkrev(node)
1811 renamed = fl.renamed(node)
1811 renamed = fl.renamed(node)
1812 rcache[fn][lr] = renamed
1812 rcache[fn][lr] = renamed
1813 if renamed:
1813 if renamed:
1814 ncache[fn][node] = renamed
1814 ncache[fn][node] = renamed
1815 if lr >= endrev:
1815 if lr >= endrev:
1816 break
1816 break
1817 if rev in rcache[fn]:
1817 if rev in rcache[fn]:
1818 return rcache[fn][rev]
1818 return rcache[fn][rev]
1819 mr = repo.manifest.rev(man)
1819 mr = repo.manifest.rev(man)
1820 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1820 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1821 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1821 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1822 if not dcache or dcache[0] != man:
1822 if not dcache or dcache[0] != man:
1823 dcache[:] = [man, repo.manifest.readdelta(man)]
1823 dcache[:] = [man, repo.manifest.readdelta(man)]
1824 if fn in dcache[1]:
1824 if fn in dcache[1]:
1825 return ncache[fn].get(dcache[1][fn])
1825 return ncache[fn].get(dcache[1][fn])
1826 return None
1826 return None
1827
1827
1828 df = False
1828 df = False
1829 if opts["date"]:
1829 if opts["date"]:
1830 df = util.matchdate(opts["date"])
1830 df = util.matchdate(opts["date"])
1831
1831
1832 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1832 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1833 for st, rev, fns in changeiter:
1833 for st, rev, fns in changeiter:
1834 if st == 'add':
1834 if st == 'add':
1835 changenode = repo.changelog.node(rev)
1835 changenode = repo.changelog.node(rev)
1836 parents = [p for p in repo.changelog.parentrevs(rev)
1836 parents = [p for p in repo.changelog.parentrevs(rev)
1837 if p != nullrev]
1837 if p != nullrev]
1838 if opts['no_merges'] and len(parents) == 2:
1838 if opts['no_merges'] and len(parents) == 2:
1839 continue
1839 continue
1840 if opts['only_merges'] and len(parents) != 2:
1840 if opts['only_merges'] and len(parents) != 2:
1841 continue
1841 continue
1842
1842
1843 if df:
1843 if df:
1844 changes = get(rev)
1844 changes = get(rev)
1845 if not df(changes[2][0]):
1845 if not df(changes[2][0]):
1846 continue
1846 continue
1847
1847
1848 if opts['keyword']:
1848 if opts['keyword']:
1849 changes = get(rev)
1849 changes = get(rev)
1850 miss = 0
1850 miss = 0
1851 for k in [kw.lower() for kw in opts['keyword']]:
1851 for k in [kw.lower() for kw in opts['keyword']]:
1852 if not (k in changes[1].lower() or
1852 if not (k in changes[1].lower() or
1853 k in changes[4].lower() or
1853 k in changes[4].lower() or
1854 k in " ".join(changes[3]).lower()):
1854 k in " ".join(changes[3]).lower()):
1855 miss = 1
1855 miss = 1
1856 break
1856 break
1857 if miss:
1857 if miss:
1858 continue
1858 continue
1859
1859
1860 copies = []
1860 copies = []
1861 if opts.get('copies') and rev:
1861 if opts.get('copies') and rev:
1862 mf = get(rev)[0]
1862 mf = get(rev)[0]
1863 for fn in get(rev)[3]:
1863 for fn in get(rev)[3]:
1864 rename = getrenamed(fn, rev, mf)
1864 rename = getrenamed(fn, rev, mf)
1865 if rename:
1865 if rename:
1866 copies.append((fn, rename[0]))
1866 copies.append((fn, rename[0]))
1867 displayer.show(rev, changenode, copies=copies)
1867 displayer.show(rev, changenode, copies=copies)
1868 elif st == 'iter':
1868 elif st == 'iter':
1869 if count == limit: break
1869 if count == limit: break
1870 if displayer.flush(rev):
1870 if displayer.flush(rev):
1871 count += 1
1871 count += 1
1872
1872
1873 def manifest(ui, repo, rev=None):
1873 def manifest(ui, repo, rev=None):
1874 """output the current or given revision of the project manifest
1874 """output the current or given revision of the project manifest
1875
1875
1876 Print a list of version controlled files for the given revision.
1876 Print a list of version controlled files for the given revision.
1877 If no revision is given, the parent of the working directory is used,
1877 If no revision is given, the parent of the working directory is used,
1878 or tip if no revision is checked out.
1878 or tip if no revision is checked out.
1879
1879
1880 The manifest is the list of files being version controlled. If no revision
1880 The manifest is the list of files being version controlled. If no revision
1881 is given then the first parent of the working directory is used.
1881 is given then the first parent of the working directory is used.
1882
1882
1883 With -v flag, print file permissions. With --debug flag, print
1883 With -v flag, print file permissions. With --debug flag, print
1884 file revision hashes.
1884 file revision hashes.
1885 """
1885 """
1886
1886
1887 m = repo.changectx(rev).manifest()
1887 m = repo.changectx(rev).manifest()
1888 files = m.keys()
1888 files = m.keys()
1889 files.sort()
1889 files.sort()
1890
1890
1891 for f in files:
1891 for f in files:
1892 if ui.debugflag:
1892 if ui.debugflag:
1893 ui.write("%40s " % hex(m[f]))
1893 ui.write("%40s " % hex(m[f]))
1894 if ui.verbose:
1894 if ui.verbose:
1895 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1895 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1896 ui.write("%s\n" % f)
1896 ui.write("%s\n" % f)
1897
1897
1898 def merge(ui, repo, node=None, force=None, rev=None):
1898 def merge(ui, repo, node=None, force=None, rev=None):
1899 """merge working directory with another revision
1899 """merge working directory with another revision
1900
1900
1901 Merge the contents of the current working directory and the
1901 Merge the contents of the current working directory and the
1902 requested revision. Files that changed between either parent are
1902 requested revision. Files that changed between either parent are
1903 marked as changed for the next commit and a commit must be
1903 marked as changed for the next commit and a commit must be
1904 performed before any further updates are allowed.
1904 performed before any further updates are allowed.
1905
1905
1906 If no revision is specified, the working directory's parent is a
1906 If no revision is specified, the working directory's parent is a
1907 head revision, and the repository contains exactly one other head,
1907 head revision, and the repository contains exactly one other head,
1908 the other head is merged with by default. Otherwise, an explicit
1908 the other head is merged with by default. Otherwise, an explicit
1909 revision to merge with must be provided.
1909 revision to merge with must be provided.
1910 """
1910 """
1911
1911
1912 if rev and node:
1912 if rev and node:
1913 raise util.Abort(_("please specify just one revision"))
1913 raise util.Abort(_("please specify just one revision"))
1914
1914
1915 if not node:
1915 if not node:
1916 node = rev
1916 node = rev
1917
1917
1918 if not node:
1918 if not node:
1919 heads = repo.heads()
1919 heads = repo.heads()
1920 if len(heads) > 2:
1920 if len(heads) > 2:
1921 raise util.Abort(_('repo has %d heads - '
1921 raise util.Abort(_('repo has %d heads - '
1922 'please merge with an explicit rev') %
1922 'please merge with an explicit rev') %
1923 len(heads))
1923 len(heads))
1924 if len(heads) == 1:
1924 if len(heads) == 1:
1925 raise util.Abort(_('there is nothing to merge - '
1925 raise util.Abort(_('there is nothing to merge - '
1926 'use "hg update" instead'))
1926 'use "hg update" instead'))
1927 parent = repo.dirstate.parents()[0]
1927 parent = repo.dirstate.parents()[0]
1928 if parent not in heads:
1928 if parent not in heads:
1929 raise util.Abort(_('working dir not at a head rev - '
1929 raise util.Abort(_('working dir not at a head rev - '
1930 'use "hg update" or merge with an explicit rev'))
1930 'use "hg update" or merge with an explicit rev'))
1931 node = parent == heads[0] and heads[-1] or heads[0]
1931 node = parent == heads[0] and heads[-1] or heads[0]
1932 return hg.merge(repo, node, force=force)
1932 return hg.merge(repo, node, force=force)
1933
1933
1934 def outgoing(ui, repo, dest=None, **opts):
1934 def outgoing(ui, repo, dest=None, **opts):
1935 """show changesets not found in destination
1935 """show changesets not found in destination
1936
1936
1937 Show changesets not found in the specified destination repository or
1937 Show changesets not found in the specified destination repository or
1938 the default push location. These are the changesets that would be pushed
1938 the default push location. These are the changesets that would be pushed
1939 if a push was requested.
1939 if a push was requested.
1940
1940
1941 See pull for valid destination format details.
1941 See pull for valid destination format details.
1942 """
1942 """
1943 dest, revs = cmdutil.parseurl(
1943 dest, revs = cmdutil.parseurl(
1944 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1944 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1945 cmdutil.setremoteconfig(ui, opts)
1945 cmdutil.setremoteconfig(ui, opts)
1946 if revs:
1946 if revs:
1947 revs = [repo.lookup(rev) for rev in revs]
1947 revs = [repo.lookup(rev) for rev in revs]
1948
1948
1949 other = hg.repository(ui, dest)
1949 other = hg.repository(ui, dest)
1950 ui.status(_('comparing with %s\n') % dest)
1950 ui.status(_('comparing with %s\n') % dest)
1951 o = repo.findoutgoing(other, force=opts['force'])
1951 o = repo.findoutgoing(other, force=opts['force'])
1952 if not o:
1952 if not o:
1953 ui.status(_("no changes found\n"))
1953 ui.status(_("no changes found\n"))
1954 return 1
1954 return 1
1955 o = repo.changelog.nodesbetween(o, revs)[0]
1955 o = repo.changelog.nodesbetween(o, revs)[0]
1956 if opts['newest_first']:
1956 if opts['newest_first']:
1957 o.reverse()
1957 o.reverse()
1958 displayer = cmdutil.show_changeset(ui, repo, opts)
1958 displayer = cmdutil.show_changeset(ui, repo, opts)
1959 for n in o:
1959 for n in o:
1960 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1960 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1961 if opts['no_merges'] and len(parents) == 2:
1961 if opts['no_merges'] and len(parents) == 2:
1962 continue
1962 continue
1963 displayer.show(changenode=n)
1963 displayer.show(changenode=n)
1964
1964
1965 def parents(ui, repo, file_=None, **opts):
1965 def parents(ui, repo, file_=None, **opts):
1966 """show the parents of the working dir or revision
1966 """show the parents of the working dir or revision
1967
1967
1968 Print the working directory's parent revisions. If a
1968 Print the working directory's parent revisions. If a
1969 revision is given via --rev, the parent of that revision
1969 revision is given via --rev, the parent of that revision
1970 will be printed. If a file argument is given, revision in
1970 will be printed. If a file argument is given, revision in
1971 which the file was last changed (before the working directory
1971 which the file was last changed (before the working directory
1972 revision or the argument to --rev if given) is printed.
1972 revision or the argument to --rev if given) is printed.
1973 """
1973 """
1974 rev = opts.get('rev')
1974 rev = opts.get('rev')
1975 if file_:
1975 if file_:
1976 ctx = repo.filectx(file_, changeid=rev)
1976 ctx = repo.filectx(file_, changeid=rev)
1977 elif rev:
1977 elif rev:
1978 ctx = repo.changectx(rev)
1978 ctx = repo.changectx(rev)
1979 else:
1979 else:
1980 ctx = repo.workingctx()
1980 ctx = repo.workingctx()
1981 p = [cp.node() for cp in ctx.parents()]
1981 p = [cp.node() for cp in ctx.parents()]
1982
1982
1983 displayer = cmdutil.show_changeset(ui, repo, opts)
1983 displayer = cmdutil.show_changeset(ui, repo, opts)
1984 for n in p:
1984 for n in p:
1985 if n != nullid:
1985 if n != nullid:
1986 displayer.show(changenode=n)
1986 displayer.show(changenode=n)
1987
1987
1988 def paths(ui, repo, search=None):
1988 def paths(ui, repo, search=None):
1989 """show definition of symbolic path names
1989 """show definition of symbolic path names
1990
1990
1991 Show definition of symbolic path name NAME. If no name is given, show
1991 Show definition of symbolic path name NAME. If no name is given, show
1992 definition of available names.
1992 definition of available names.
1993
1993
1994 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1994 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1995 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1995 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1996 """
1996 """
1997 if search:
1997 if search:
1998 for name, path in ui.configitems("paths"):
1998 for name, path in ui.configitems("paths"):
1999 if name == search:
1999 if name == search:
2000 ui.write("%s\n" % path)
2000 ui.write("%s\n" % path)
2001 return
2001 return
2002 ui.warn(_("not found!\n"))
2002 ui.warn(_("not found!\n"))
2003 return 1
2003 return 1
2004 else:
2004 else:
2005 for name, path in ui.configitems("paths"):
2005 for name, path in ui.configitems("paths"):
2006 ui.write("%s = %s\n" % (name, path))
2006 ui.write("%s = %s\n" % (name, path))
2007
2007
2008 def postincoming(ui, repo, modheads, optupdate):
2008 def postincoming(ui, repo, modheads, optupdate):
2009 if modheads == 0:
2009 if modheads == 0:
2010 return
2010 return
2011 if optupdate:
2011 if optupdate:
2012 if modheads == 1:
2012 if modheads == 1:
2013 return hg.update(repo, repo.changelog.tip()) # update
2013 return hg.update(repo, repo.changelog.tip()) # update
2014 else:
2014 else:
2015 ui.status(_("not updating, since new heads added\n"))
2015 ui.status(_("not updating, since new heads added\n"))
2016 if modheads > 1:
2016 if modheads > 1:
2017 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2017 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2018 else:
2018 else:
2019 ui.status(_("(run 'hg update' to get a working copy)\n"))
2019 ui.status(_("(run 'hg update' to get a working copy)\n"))
2020
2020
2021 def pull(ui, repo, source="default", **opts):
2021 def pull(ui, repo, source="default", **opts):
2022 """pull changes from the specified source
2022 """pull changes from the specified source
2023
2023
2024 Pull changes from a remote repository to a local one.
2024 Pull changes from a remote repository to a local one.
2025
2025
2026 This finds all changes from the repository at the specified path
2026 This finds all changes from the repository at the specified path
2027 or URL and adds them to the local repository. By default, this
2027 or URL and adds them to the local repository. By default, this
2028 does not update the copy of the project in the working directory.
2028 does not update the copy of the project in the working directory.
2029
2029
2030 Valid URLs are of the form:
2030 Valid URLs are of the form:
2031
2031
2032 local/filesystem/path (or file://local/filesystem/path)
2032 local/filesystem/path (or file://local/filesystem/path)
2033 http://[user@]host[:port]/[path]
2033 http://[user@]host[:port]/[path]
2034 https://[user@]host[:port]/[path]
2034 https://[user@]host[:port]/[path]
2035 ssh://[user@]host[:port]/[path]
2035 ssh://[user@]host[:port]/[path]
2036 static-http://host[:port]/[path]
2036 static-http://host[:port]/[path]
2037
2037
2038 Paths in the local filesystem can either point to Mercurial
2038 Paths in the local filesystem can either point to Mercurial
2039 repositories or to bundle files (as created by 'hg bundle' or
2039 repositories or to bundle files (as created by 'hg bundle' or
2040 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2040 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2041 allows access to a Mercurial repository where you simply use a web
2041 allows access to a Mercurial repository where you simply use a web
2042 server to publish the .hg directory as static content.
2042 server to publish the .hg directory as static content.
2043
2043
2044 An optional identifier after # indicates a particular branch, tag,
2044 An optional identifier after # indicates a particular branch, tag,
2045 or changeset to pull.
2045 or changeset to pull.
2046
2046
2047 Some notes about using SSH with Mercurial:
2047 Some notes about using SSH with Mercurial:
2048 - SSH requires an accessible shell account on the destination machine
2048 - SSH requires an accessible shell account on the destination machine
2049 and a copy of hg in the remote path or specified with as remotecmd.
2049 and a copy of hg in the remote path or specified with as remotecmd.
2050 - path is relative to the remote user's home directory by default.
2050 - path is relative to the remote user's home directory by default.
2051 Use an extra slash at the start of a path to specify an absolute path:
2051 Use an extra slash at the start of a path to specify an absolute path:
2052 ssh://example.com//tmp/repository
2052 ssh://example.com//tmp/repository
2053 - Mercurial doesn't use its own compression via SSH; the right thing
2053 - Mercurial doesn't use its own compression via SSH; the right thing
2054 to do is to configure it in your ~/.ssh/config, e.g.:
2054 to do is to configure it in your ~/.ssh/config, e.g.:
2055 Host *.mylocalnetwork.example.com
2055 Host *.mylocalnetwork.example.com
2056 Compression no
2056 Compression no
2057 Host *
2057 Host *
2058 Compression yes
2058 Compression yes
2059 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2059 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2060 with the --ssh command line option.
2060 with the --ssh command line option.
2061 """
2061 """
2062 source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
2062 source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
2063 cmdutil.setremoteconfig(ui, opts)
2063 cmdutil.setremoteconfig(ui, opts)
2064
2064
2065 other = hg.repository(ui, source)
2065 other = hg.repository(ui, source)
2066 ui.status(_('pulling from %s\n') % (source))
2066 ui.status(_('pulling from %s\n') % (source))
2067 if revs:
2067 if revs:
2068 if 'lookup' in other.capabilities:
2068 if 'lookup' in other.capabilities:
2069 revs = [other.lookup(rev) for rev in revs]
2069 revs = [other.lookup(rev) for rev in revs]
2070 else:
2070 else:
2071 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
2071 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
2072 raise util.Abort(error)
2072 raise util.Abort(error)
2073
2073
2074 modheads = repo.pull(other, heads=revs, force=opts['force'])
2074 modheads = repo.pull(other, heads=revs, force=opts['force'])
2075 return postincoming(ui, repo, modheads, opts['update'])
2075 return postincoming(ui, repo, modheads, opts['update'])
2076
2076
2077 def push(ui, repo, dest=None, **opts):
2077 def push(ui, repo, dest=None, **opts):
2078 """push changes to the specified destination
2078 """push changes to the specified destination
2079
2079
2080 Push changes from the local repository to the given destination.
2080 Push changes from the local repository to the given destination.
2081
2081
2082 This is the symmetrical operation for pull. It helps to move
2082 This is the symmetrical operation for pull. It helps to move
2083 changes from the current repository to a different one. If the
2083 changes from the current repository to a different one. If the
2084 destination is local this is identical to a pull in that directory
2084 destination is local this is identical to a pull in that directory
2085 from the current one.
2085 from the current one.
2086
2086
2087 By default, push will refuse to run if it detects the result would
2087 By default, push will refuse to run if it detects the result would
2088 increase the number of remote heads. This generally indicates the
2088 increase the number of remote heads. This generally indicates the
2089 the client has forgotten to sync and merge before pushing.
2089 the client has forgotten to sync and merge before pushing.
2090
2090
2091 Valid URLs are of the form:
2091 Valid URLs are of the form:
2092
2092
2093 local/filesystem/path (or file://local/filesystem/path)
2093 local/filesystem/path (or file://local/filesystem/path)
2094 ssh://[user@]host[:port]/[path]
2094 ssh://[user@]host[:port]/[path]
2095 http://[user@]host[:port]/[path]
2095 http://[user@]host[:port]/[path]
2096 https://[user@]host[:port]/[path]
2096 https://[user@]host[:port]/[path]
2097
2097
2098 An optional identifier after # indicates a particular branch, tag,
2098 An optional identifier after # indicates a particular branch, tag,
2099 or changeset to push.
2099 or changeset to push.
2100
2100
2101 Look at the help text for the pull command for important details
2101 Look at the help text for the pull command for important details
2102 about ssh:// URLs.
2102 about ssh:// URLs.
2103
2103
2104 Pushing to http:// and https:// URLs is only possible, if this
2104 Pushing to http:// and https:// URLs is only possible, if this
2105 feature is explicitly enabled on the remote Mercurial server.
2105 feature is explicitly enabled on the remote Mercurial server.
2106 """
2106 """
2107 dest, revs = cmdutil.parseurl(
2107 dest, revs = cmdutil.parseurl(
2108 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2108 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2109 cmdutil.setremoteconfig(ui, opts)
2109 cmdutil.setremoteconfig(ui, opts)
2110
2110
2111 other = hg.repository(ui, dest)
2111 other = hg.repository(ui, dest)
2112 ui.status('pushing to %s\n' % (dest))
2112 ui.status('pushing to %s\n' % (dest))
2113 if revs:
2113 if revs:
2114 revs = [repo.lookup(rev) for rev in revs]
2114 revs = [repo.lookup(rev) for rev in revs]
2115 r = repo.push(other, opts['force'], revs=revs)
2115 r = repo.push(other, opts['force'], revs=revs)
2116 return r == 0
2116 return r == 0
2117
2117
2118 def rawcommit(ui, repo, *pats, **opts):
2118 def rawcommit(ui, repo, *pats, **opts):
2119 """raw commit interface (DEPRECATED)
2119 """raw commit interface (DEPRECATED)
2120
2120
2121 (DEPRECATED)
2121 (DEPRECATED)
2122 Lowlevel commit, for use in helper scripts.
2122 Lowlevel commit, for use in helper scripts.
2123
2123
2124 This command is not intended to be used by normal users, as it is
2124 This command is not intended to be used by normal users, as it is
2125 primarily useful for importing from other SCMs.
2125 primarily useful for importing from other SCMs.
2126
2126
2127 This command is now deprecated and will be removed in a future
2127 This command is now deprecated and will be removed in a future
2128 release, please use debugsetparents and commit instead.
2128 release, please use debugsetparents and commit instead.
2129 """
2129 """
2130
2130
2131 ui.warn(_("(the rawcommit command is deprecated)\n"))
2131 ui.warn(_("(the rawcommit command is deprecated)\n"))
2132
2132
2133 message = cmdutil.logmessage(opts)
2133 message = cmdutil.logmessage(opts)
2134
2134
2135 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2135 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2136 if opts['files']:
2136 if opts['files']:
2137 files += open(opts['files']).read().splitlines()
2137 files += open(opts['files']).read().splitlines()
2138
2138
2139 parents = [repo.lookup(p) for p in opts['parent']]
2139 parents = [repo.lookup(p) for p in opts['parent']]
2140
2140
2141 try:
2141 try:
2142 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2142 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2143 except ValueError, inst:
2143 except ValueError, inst:
2144 raise util.Abort(str(inst))
2144 raise util.Abort(str(inst))
2145
2145
2146 def recover(ui, repo):
2146 def recover(ui, repo):
2147 """roll back an interrupted transaction
2147 """roll back an interrupted transaction
2148
2148
2149 Recover from an interrupted commit or pull.
2149 Recover from an interrupted commit or pull.
2150
2150
2151 This command tries to fix the repository status after an interrupted
2151 This command tries to fix the repository status after an interrupted
2152 operation. It should only be necessary when Mercurial suggests it.
2152 operation. It should only be necessary when Mercurial suggests it.
2153 """
2153 """
2154 if repo.recover():
2154 if repo.recover():
2155 return hg.verify(repo)
2155 return hg.verify(repo)
2156 return 1
2156 return 1
2157
2157
2158 def remove(ui, repo, *pats, **opts):
2158 def remove(ui, repo, *pats, **opts):
2159 """remove the specified files on the next commit
2159 """remove the specified files on the next commit
2160
2160
2161 Schedule the indicated files for removal from the repository.
2161 Schedule the indicated files for removal from the repository.
2162
2162
2163 This only removes files from the current branch, not from the
2163 This only removes files from the current branch, not from the
2164 entire project history. If the files still exist in the working
2164 entire project history. If the files still exist in the working
2165 directory, they will be deleted from it. If invoked with --after,
2165 directory, they will be deleted from it. If invoked with --after,
2166 files are marked as removed, but not actually unlinked unless --force
2166 files are marked as removed, but not actually unlinked unless --force
2167 is also given. Without exact file names, --after will only mark
2167 is also given. Without exact file names, --after will only mark
2168 files as removed if they are no longer in the working directory.
2168 files as removed if they are no longer in the working directory.
2169
2169
2170 This command schedules the files to be removed at the next commit.
2170 This command schedules the files to be removed at the next commit.
2171 To undo a remove before that, see hg revert.
2171 To undo a remove before that, see hg revert.
2172
2172
2173 Modified files and added files are not removed by default. To
2173 Modified files and added files are not removed by default. To
2174 remove them, use the -f/--force option.
2174 remove them, use the -f/--force option.
2175 """
2175 """
2176 names = []
2176 names = []
2177 if not opts['after'] and not pats:
2177 if not opts['after'] and not pats:
2178 raise util.Abort(_('no files specified'))
2178 raise util.Abort(_('no files specified'))
2179 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2179 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2180 exact = dict.fromkeys(files)
2180 exact = dict.fromkeys(files)
2181 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2181 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2182 modified, added, removed, deleted, unknown = mardu
2182 modified, added, removed, deleted, unknown = mardu
2183 remove, forget = [], []
2183 remove, forget = [], []
2184 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2184 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2185 reason = None
2185 reason = None
2186 if abs in modified and not opts['force']:
2186 if abs in modified and not opts['force']:
2187 reason = _('is modified (use -f to force removal)')
2187 reason = _('is modified (use -f to force removal)')
2188 elif abs in added:
2188 elif abs in added:
2189 if opts['force']:
2189 if opts['force']:
2190 forget.append(abs)
2190 forget.append(abs)
2191 continue
2191 continue
2192 reason = _('has been marked for add (use -f to force removal)')
2192 reason = _('has been marked for add (use -f to force removal)')
2193 elif repo.dirstate.state(abs) == '?':
2193 elif repo.dirstate.state(abs) == '?':
2194 reason = _('is not managed')
2194 reason = _('is not managed')
2195 elif opts['after'] and not exact and abs not in deleted:
2195 elif opts['after'] and not exact and abs not in deleted:
2196 continue
2196 continue
2197 elif abs in removed:
2197 elif abs in removed:
2198 continue
2198 continue
2199 if reason:
2199 if reason:
2200 if exact:
2200 if exact:
2201 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2201 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2202 else:
2202 else:
2203 if ui.verbose or not exact:
2203 if ui.verbose or not exact:
2204 ui.status(_('removing %s\n') % rel)
2204 ui.status(_('removing %s\n') % rel)
2205 remove.append(abs)
2205 remove.append(abs)
2206 repo.forget(forget)
2206 repo.forget(forget)
2207 repo.remove(remove, unlink=opts['force'] or not opts['after'])
2207 repo.remove(remove, unlink=opts['force'] or not opts['after'])
2208
2208
2209 def rename(ui, repo, *pats, **opts):
2209 def rename(ui, repo, *pats, **opts):
2210 """rename files; equivalent of copy + remove
2210 """rename files; equivalent of copy + remove
2211
2211
2212 Mark dest as copies of sources; mark sources for deletion. If
2212 Mark dest as copies of sources; mark sources for deletion. If
2213 dest is a directory, copies are put in that directory. If dest is
2213 dest is a directory, copies are put in that directory. If dest is
2214 a file, there can only be one source.
2214 a file, there can only be one source.
2215
2215
2216 By default, this command copies the contents of files as they
2216 By default, this command copies the contents of files as they
2217 stand in the working directory. If invoked with --after, the
2217 stand in the working directory. If invoked with --after, the
2218 operation is recorded, but no copying is performed.
2218 operation is recorded, but no copying is performed.
2219
2219
2220 This command takes effect in the next commit. To undo a rename
2220 This command takes effect in the next commit. To undo a rename
2221 before that, see hg revert.
2221 before that, see hg revert.
2222 """
2222 """
2223 wlock = repo.wlock(0)
2223 wlock = repo.wlock(0)
2224 errs, copied = docopy(ui, repo, pats, opts, wlock)
2224 errs, copied = docopy(ui, repo, pats, opts, wlock)
2225 names = []
2225 names = []
2226 for abs, rel, exact in copied:
2226 for abs, rel, exact in copied:
2227 if ui.verbose or not exact:
2227 if ui.verbose or not exact:
2228 ui.status(_('removing %s\n') % rel)
2228 ui.status(_('removing %s\n') % rel)
2229 names.append(abs)
2229 names.append(abs)
2230 if not opts.get('dry_run'):
2230 if not opts.get('dry_run'):
2231 repo.remove(names, True, wlock=wlock)
2231 repo.remove(names, True, wlock=wlock)
2232 return errs
2232 return errs
2233
2233
2234 def revert(ui, repo, *pats, **opts):
2234 def revert(ui, repo, *pats, **opts):
2235 """revert files or dirs to their states as of some revision
2235 """revert files or dirs to their states as of some revision
2236
2236
2237 With no revision specified, revert the named files or directories
2237 With no revision specified, revert the named files or directories
2238 to the contents they had in the parent of the working directory.
2238 to the contents they had in the parent of the working directory.
2239 This restores the contents of the affected files to an unmodified
2239 This restores the contents of the affected files to an unmodified
2240 state and unschedules adds, removes, copies, and renames. If the
2240 state and unschedules adds, removes, copies, and renames. If the
2241 working directory has two parents, you must explicitly specify the
2241 working directory has two parents, you must explicitly specify the
2242 revision to revert to.
2242 revision to revert to.
2243
2243
2244 Modified files are saved with a .orig suffix before reverting.
2244 Modified files are saved with a .orig suffix before reverting.
2245 To disable these backups, use --no-backup.
2245 To disable these backups, use --no-backup.
2246
2246
2247 Using the -r option, revert the given files or directories to their
2247 Using the -r option, revert the given files or directories to their
2248 contents as of a specific revision. This can be helpful to "roll
2248 contents as of a specific revision. This can be helpful to "roll
2249 back" some or all of a change that should not have been committed.
2249 back" some or all of a change that should not have been committed.
2250
2250
2251 Revert modifies the working directory. It does not commit any
2251 Revert modifies the working directory. It does not commit any
2252 changes, or change the parent of the working directory. If you
2252 changes, or change the parent of the working directory. If you
2253 revert to a revision other than the parent of the working
2253 revert to a revision other than the parent of the working
2254 directory, the reverted files will thus appear modified
2254 directory, the reverted files will thus appear modified
2255 afterwards.
2255 afterwards.
2256
2256
2257 If a file has been deleted, it is recreated. If the executable
2257 If a file has been deleted, it is restored. If the executable
2258 mode of a file was changed, it is reset.
2258 mode of a file was changed, it is reset.
2259
2259
2260 If names are given, all files matching the names are reverted.
2260 If names are given, all files matching the names are reverted.
2261
2261
2262 If no arguments are given, no files are reverted.
2262 If no arguments are given, no files are reverted.
2263 """
2263 """
2264
2264
2265 if opts["date"]:
2265 if opts["date"]:
2266 if opts["rev"]:
2266 if opts["rev"]:
2267 raise util.Abort(_("you can't specify a revision and a date"))
2267 raise util.Abort(_("you can't specify a revision and a date"))
2268 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2268 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2269
2269
2270 if not pats and not opts['all']:
2270 if not pats and not opts['all']:
2271 raise util.Abort(_('no files or directories specified; '
2271 raise util.Abort(_('no files or directories specified; '
2272 'use --all to revert the whole repo'))
2272 'use --all to revert the whole repo'))
2273
2273
2274 parent, p2 = repo.dirstate.parents()
2274 parent, p2 = repo.dirstate.parents()
2275 if not opts['rev'] and p2 != nullid:
2275 if not opts['rev'] and p2 != nullid:
2276 raise util.Abort(_('uncommitted merge - please provide a '
2276 raise util.Abort(_('uncommitted merge - please provide a '
2277 'specific revision'))
2277 'specific revision'))
2278 ctx = repo.changectx(opts['rev'])
2278 ctx = repo.changectx(opts['rev'])
2279 node = ctx.node()
2279 node = ctx.node()
2280 mf = ctx.manifest()
2280 mf = ctx.manifest()
2281 if node == parent:
2281 if node == parent:
2282 pmf = mf
2282 pmf = mf
2283 else:
2283 else:
2284 pmf = None
2284 pmf = None
2285
2285
2286 wlock = repo.wlock()
2286 wlock = repo.wlock()
2287
2287
2288 # need all matching names in dirstate and manifest of target rev,
2288 # need all matching names in dirstate and manifest of target rev,
2289 # so have to walk both. do not print errors if files exist in one
2289 # so have to walk both. do not print errors if files exist in one
2290 # but not other.
2290 # but not other.
2291
2291
2292 names = {}
2292 names = {}
2293 target_only = {}
2293 target_only = {}
2294
2294
2295 # walk dirstate.
2295 # walk dirstate.
2296
2296
2297 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2297 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2298 badmatch=mf.has_key):
2298 badmatch=mf.has_key):
2299 names[abs] = (rel, exact)
2299 names[abs] = (rel, exact)
2300 if src == 'b':
2300 if src == 'b':
2301 target_only[abs] = True
2301 target_only[abs] = True
2302
2302
2303 # walk target manifest.
2303 # walk target manifest.
2304
2304
2305 def badmatch(path):
2305 def badmatch(path):
2306 if path in names:
2306 if path in names:
2307 return True
2307 return True
2308 path_ = path + '/'
2308 path_ = path + '/'
2309 for f in names:
2309 for f in names:
2310 if f.startswith(path_):
2310 if f.startswith(path_):
2311 return True
2311 return True
2312 return False
2312 return False
2313
2313
2314 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2314 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2315 badmatch=badmatch):
2315 badmatch=badmatch):
2316 if abs in names or src == 'b':
2316 if abs in names or src == 'b':
2317 continue
2317 continue
2318 names[abs] = (rel, exact)
2318 names[abs] = (rel, exact)
2319 target_only[abs] = True
2319 target_only[abs] = True
2320
2320
2321 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2321 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2322 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2322 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2323
2323
2324 revert = ([], _('reverting %s\n'))
2324 revert = ([], _('reverting %s\n'))
2325 add = ([], _('adding %s\n'))
2325 add = ([], _('adding %s\n'))
2326 remove = ([], _('removing %s\n'))
2326 remove = ([], _('removing %s\n'))
2327 forget = ([], _('forgetting %s\n'))
2327 forget = ([], _('forgetting %s\n'))
2328 undelete = ([], _('undeleting %s\n'))
2328 undelete = ([], _('undeleting %s\n'))
2329 update = {}
2329 update = {}
2330
2330
2331 disptable = (
2331 disptable = (
2332 # dispatch table:
2332 # dispatch table:
2333 # file state
2333 # file state
2334 # action if in target manifest
2334 # action if in target manifest
2335 # action if not in target manifest
2335 # action if not in target manifest
2336 # make backup if in target manifest
2336 # make backup if in target manifest
2337 # make backup if not in target manifest
2337 # make backup if not in target manifest
2338 (modified, revert, remove, True, True),
2338 (modified, revert, remove, True, True),
2339 (added, revert, forget, True, False),
2339 (added, revert, forget, True, False),
2340 (removed, undelete, None, False, False),
2340 (removed, undelete, None, False, False),
2341 (deleted, revert, remove, False, False),
2341 (deleted, revert, remove, False, False),
2342 (unknown, add, None, True, False),
2342 (unknown, add, None, True, False),
2343 (target_only, add, None, False, False),
2343 (target_only, add, None, False, False),
2344 )
2344 )
2345
2345
2346 entries = names.items()
2346 entries = names.items()
2347 entries.sort()
2347 entries.sort()
2348
2348
2349 for abs, (rel, exact) in entries:
2349 for abs, (rel, exact) in entries:
2350 mfentry = mf.get(abs)
2350 mfentry = mf.get(abs)
2351 target = repo.wjoin(abs)
2351 target = repo.wjoin(abs)
2352 def handle(xlist, dobackup):
2352 def handle(xlist, dobackup):
2353 xlist[0].append(abs)
2353 xlist[0].append(abs)
2354 update[abs] = 1
2354 update[abs] = 1
2355 if dobackup and not opts['no_backup'] and util.lexists(target):
2355 if dobackup and not opts['no_backup'] and util.lexists(target):
2356 bakname = "%s.orig" % rel
2356 bakname = "%s.orig" % rel
2357 ui.note(_('saving current version of %s as %s\n') %
2357 ui.note(_('saving current version of %s as %s\n') %
2358 (rel, bakname))
2358 (rel, bakname))
2359 if not opts.get('dry_run'):
2359 if not opts.get('dry_run'):
2360 util.copyfile(target, bakname)
2360 util.copyfile(target, bakname)
2361 if ui.verbose or not exact:
2361 if ui.verbose or not exact:
2362 ui.status(xlist[1] % rel)
2362 ui.status(xlist[1] % rel)
2363 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2363 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2364 if abs not in table: continue
2364 if abs not in table: continue
2365 # file has changed in dirstate
2365 # file has changed in dirstate
2366 if mfentry:
2366 if mfentry:
2367 handle(hitlist, backuphit)
2367 handle(hitlist, backuphit)
2368 elif misslist is not None:
2368 elif misslist is not None:
2369 handle(misslist, backupmiss)
2369 handle(misslist, backupmiss)
2370 else:
2370 else:
2371 if exact: ui.warn(_('file not managed: %s\n') % rel)
2371 if exact: ui.warn(_('file not managed: %s\n') % rel)
2372 break
2372 break
2373 else:
2373 else:
2374 # file has not changed in dirstate
2374 # file has not changed in dirstate
2375 if node == parent:
2375 if node == parent:
2376 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2376 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2377 continue
2377 continue
2378 if pmf is None:
2378 if pmf is None:
2379 # only need parent manifest in this unlikely case,
2379 # only need parent manifest in this unlikely case,
2380 # so do not read by default
2380 # so do not read by default
2381 pmf = repo.changectx(parent).manifest()
2381 pmf = repo.changectx(parent).manifest()
2382 if abs in pmf:
2382 if abs in pmf:
2383 if mfentry:
2383 if mfentry:
2384 # if version of file is same in parent and target
2384 # if version of file is same in parent and target
2385 # manifests, do nothing
2385 # manifests, do nothing
2386 if pmf[abs] != mfentry:
2386 if pmf[abs] != mfentry:
2387 handle(revert, False)
2387 handle(revert, False)
2388 else:
2388 else:
2389 handle(remove, False)
2389 handle(remove, False)
2390
2390
2391 if not opts.get('dry_run'):
2391 if not opts.get('dry_run'):
2392 repo.dirstate.forget(forget[0])
2392 repo.dirstate.forget(forget[0])
2393 r = hg.revert(repo, node, update.has_key, wlock)
2393 r = hg.revert(repo, node, update.has_key, wlock)
2394 repo.dirstate.update(add[0], 'a')
2394 repo.dirstate.update(add[0], 'a')
2395 repo.dirstate.update(undelete[0], 'n')
2395 repo.dirstate.update(undelete[0], 'n')
2396 repo.dirstate.update(remove[0], 'r')
2396 repo.dirstate.update(remove[0], 'r')
2397 return r
2397 return r
2398
2398
2399 def rollback(ui, repo):
2399 def rollback(ui, repo):
2400 """roll back the last transaction in this repository
2400 """roll back the last transaction in this repository
2401
2401
2402 Roll back the last transaction in this repository, restoring the
2402 Roll back the last transaction in this repository, restoring the
2403 project to its state prior to the transaction.
2403 project to its state prior to the transaction.
2404
2404
2405 Transactions are used to encapsulate the effects of all commands
2405 Transactions are used to encapsulate the effects of all commands
2406 that create new changesets or propagate existing changesets into a
2406 that create new changesets or propagate existing changesets into a
2407 repository. For example, the following commands are transactional,
2407 repository. For example, the following commands are transactional,
2408 and their effects can be rolled back:
2408 and their effects can be rolled back:
2409
2409
2410 commit
2410 commit
2411 import
2411 import
2412 pull
2412 pull
2413 push (with this repository as destination)
2413 push (with this repository as destination)
2414 unbundle
2414 unbundle
2415
2415
2416 This command should be used with care. There is only one level of
2416 This command should be used with care. There is only one level of
2417 rollback, and there is no way to undo a rollback. It will also
2417 rollback, and there is no way to undo a rollback. It will also
2418 restore the dirstate at the time of the last transaction, which
2418 restore the dirstate at the time of the last transaction, which
2419 may lose subsequent dirstate changes.
2419 may lose subsequent dirstate changes.
2420
2420
2421 This command is not intended for use on public repositories. Once
2421 This command is not intended for use on public repositories. Once
2422 changes are visible for pull by other users, rolling a transaction
2422 changes are visible for pull by other users, rolling a transaction
2423 back locally is ineffective (someone else may already have pulled
2423 back locally is ineffective (someone else may already have pulled
2424 the changes). Furthermore, a race is possible with readers of the
2424 the changes). Furthermore, a race is possible with readers of the
2425 repository; for example an in-progress pull from the repository
2425 repository; for example an in-progress pull from the repository
2426 may fail if a rollback is performed.
2426 may fail if a rollback is performed.
2427 """
2427 """
2428 repo.rollback()
2428 repo.rollback()
2429
2429
2430 def root(ui, repo):
2430 def root(ui, repo):
2431 """print the root (top) of the current working dir
2431 """print the root (top) of the current working dir
2432
2432
2433 Print the root directory of the current repository.
2433 Print the root directory of the current repository.
2434 """
2434 """
2435 ui.write(repo.root + "\n")
2435 ui.write(repo.root + "\n")
2436
2436
2437 def serve(ui, repo, **opts):
2437 def serve(ui, repo, **opts):
2438 """export the repository via HTTP
2438 """export the repository via HTTP
2439
2439
2440 Start a local HTTP repository browser and pull server.
2440 Start a local HTTP repository browser and pull server.
2441
2441
2442 By default, the server logs accesses to stdout and errors to
2442 By default, the server logs accesses to stdout and errors to
2443 stderr. Use the "-A" and "-E" options to log to files.
2443 stderr. Use the "-A" and "-E" options to log to files.
2444 """
2444 """
2445
2445
2446 if opts["stdio"]:
2446 if opts["stdio"]:
2447 if repo is None:
2447 if repo is None:
2448 raise hg.RepoError(_("There is no Mercurial repository here"
2448 raise hg.RepoError(_("There is no Mercurial repository here"
2449 " (.hg not found)"))
2449 " (.hg not found)"))
2450 s = sshserver.sshserver(ui, repo)
2450 s = sshserver.sshserver(ui, repo)
2451 s.serve_forever()
2451 s.serve_forever()
2452
2452
2453 parentui = ui.parentui or ui
2453 parentui = ui.parentui or ui
2454 optlist = ("name templates style address port ipv6"
2454 optlist = ("name templates style address port ipv6"
2455 " accesslog errorlog webdir_conf")
2455 " accesslog errorlog webdir_conf")
2456 for o in optlist.split():
2456 for o in optlist.split():
2457 if opts[o]:
2457 if opts[o]:
2458 parentui.setconfig("web", o, str(opts[o]))
2458 parentui.setconfig("web", o, str(opts[o]))
2459
2459
2460 if repo is None and not ui.config("web", "webdir_conf"):
2460 if repo is None and not ui.config("web", "webdir_conf"):
2461 raise hg.RepoError(_("There is no Mercurial repository here"
2461 raise hg.RepoError(_("There is no Mercurial repository here"
2462 " (.hg not found)"))
2462 " (.hg not found)"))
2463
2463
2464 class service:
2464 class service:
2465 def init(self):
2465 def init(self):
2466 util.set_signal_handler()
2466 util.set_signal_handler()
2467 try:
2467 try:
2468 self.httpd = hgweb.server.create_server(parentui, repo)
2468 self.httpd = hgweb.server.create_server(parentui, repo)
2469 except socket.error, inst:
2469 except socket.error, inst:
2470 raise util.Abort(_('cannot start server: ') + inst.args[1])
2470 raise util.Abort(_('cannot start server: ') + inst.args[1])
2471
2471
2472 if not ui.verbose: return
2472 if not ui.verbose: return
2473
2473
2474 if self.httpd.port != 80:
2474 if self.httpd.port != 80:
2475 ui.status(_('listening at http://%s:%d/\n') %
2475 ui.status(_('listening at http://%s:%d/\n') %
2476 (self.httpd.addr, self.httpd.port))
2476 (self.httpd.addr, self.httpd.port))
2477 else:
2477 else:
2478 ui.status(_('listening at http://%s/\n') % self.httpd.addr)
2478 ui.status(_('listening at http://%s/\n') % self.httpd.addr)
2479
2479
2480 def run(self):
2480 def run(self):
2481 self.httpd.serve_forever()
2481 self.httpd.serve_forever()
2482
2482
2483 service = service()
2483 service = service()
2484
2484
2485 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2485 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2486
2486
2487 def status(ui, repo, *pats, **opts):
2487 def status(ui, repo, *pats, **opts):
2488 """show changed files in the working directory
2488 """show changed files in the working directory
2489
2489
2490 Show status of files in the repository. If names are given, only
2490 Show status of files in the repository. If names are given, only
2491 files that match are shown. Files that are clean or ignored, are
2491 files that match are shown. Files that are clean or ignored, are
2492 not listed unless -c (clean), -i (ignored) or -A is given.
2492 not listed unless -c (clean), -i (ignored) or -A is given.
2493
2493
2494 NOTE: status may appear to disagree with diff if permissions have
2494 NOTE: status may appear to disagree with diff if permissions have
2495 changed or a merge has occurred. The standard diff format does not
2495 changed or a merge has occurred. The standard diff format does not
2496 report permission changes and diff only reports changes relative
2496 report permission changes and diff only reports changes relative
2497 to one merge parent.
2497 to one merge parent.
2498
2498
2499 If one revision is given, it is used as the base revision.
2499 If one revision is given, it is used as the base revision.
2500 If two revisions are given, the difference between them is shown.
2500 If two revisions are given, the difference between them is shown.
2501
2501
2502 The codes used to show the status of files are:
2502 The codes used to show the status of files are:
2503 M = modified
2503 M = modified
2504 A = added
2504 A = added
2505 R = removed
2505 R = removed
2506 C = clean
2506 C = clean
2507 ! = deleted, but still tracked
2507 ! = deleted, but still tracked
2508 ? = not tracked
2508 ? = not tracked
2509 I = ignored (not shown by default)
2509 I = ignored (not shown by default)
2510 = the previous added file was copied from here
2510 = the previous added file was copied from here
2511 """
2511 """
2512
2512
2513 all = opts['all']
2513 all = opts['all']
2514 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2514 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2515
2515
2516 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2516 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2517 cwd = (pats and repo.getcwd()) or ''
2517 cwd = (pats and repo.getcwd()) or ''
2518 modified, added, removed, deleted, unknown, ignored, clean = [
2518 modified, added, removed, deleted, unknown, ignored, clean = [
2519 n for n in repo.status(node1=node1, node2=node2, files=files,
2519 n for n in repo.status(node1=node1, node2=node2, files=files,
2520 match=matchfn,
2520 match=matchfn,
2521 list_ignored=all or opts['ignored'],
2521 list_ignored=all or opts['ignored'],
2522 list_clean=all or opts['clean'])]
2522 list_clean=all or opts['clean'])]
2523
2523
2524 changetypes = (('modified', 'M', modified),
2524 changetypes = (('modified', 'M', modified),
2525 ('added', 'A', added),
2525 ('added', 'A', added),
2526 ('removed', 'R', removed),
2526 ('removed', 'R', removed),
2527 ('deleted', '!', deleted),
2527 ('deleted', '!', deleted),
2528 ('unknown', '?', unknown),
2528 ('unknown', '?', unknown),
2529 ('ignored', 'I', ignored))
2529 ('ignored', 'I', ignored))
2530
2530
2531 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2531 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2532
2532
2533 end = opts['print0'] and '\0' or '\n'
2533 end = opts['print0'] and '\0' or '\n'
2534
2534
2535 for opt, char, changes in ([ct for ct in explicit_changetypes
2535 for opt, char, changes in ([ct for ct in explicit_changetypes
2536 if all or opts[ct[0]]]
2536 if all or opts[ct[0]]]
2537 or changetypes):
2537 or changetypes):
2538 if opts['no_status']:
2538 if opts['no_status']:
2539 format = "%%s%s" % end
2539 format = "%%s%s" % end
2540 else:
2540 else:
2541 format = "%s %%s%s" % (char, end)
2541 format = "%s %%s%s" % (char, end)
2542
2542
2543 for f in changes:
2543 for f in changes:
2544 ui.write(format % repo.pathto(f, cwd))
2544 ui.write(format % repo.pathto(f, cwd))
2545 if ((all or opts.get('copies')) and not opts.get('no_status')):
2545 if ((all or opts.get('copies')) and not opts.get('no_status')):
2546 copied = repo.dirstate.copied(f)
2546 copied = repo.dirstate.copied(f)
2547 if copied:
2547 if copied:
2548 ui.write(' %s%s' % (repo.pathto(copied, cwd), end))
2548 ui.write(' %s%s' % (repo.pathto(copied, cwd), end))
2549
2549
2550 def tag(ui, repo, name, rev_=None, **opts):
2550 def tag(ui, repo, name, rev_=None, **opts):
2551 """add a tag for the current or given revision
2551 """add a tag for the current or given revision
2552
2552
2553 Name a particular revision using <name>.
2553 Name a particular revision using <name>.
2554
2554
2555 Tags are used to name particular revisions of the repository and are
2555 Tags are used to name particular revisions of the repository and are
2556 very useful to compare different revision, to go back to significant
2556 very useful to compare different revision, to go back to significant
2557 earlier versions or to mark branch points as releases, etc.
2557 earlier versions or to mark branch points as releases, etc.
2558
2558
2559 If no revision is given, the parent of the working directory is used,
2559 If no revision is given, the parent of the working directory is used,
2560 or tip if no revision is checked out.
2560 or tip if no revision is checked out.
2561
2561
2562 To facilitate version control, distribution, and merging of tags,
2562 To facilitate version control, distribution, and merging of tags,
2563 they are stored as a file named ".hgtags" which is managed
2563 they are stored as a file named ".hgtags" which is managed
2564 similarly to other project files and can be hand-edited if
2564 similarly to other project files and can be hand-edited if
2565 necessary. The file '.hg/localtags' is used for local tags (not
2565 necessary. The file '.hg/localtags' is used for local tags (not
2566 shared among repositories).
2566 shared among repositories).
2567 """
2567 """
2568 if name in ['tip', '.', 'null']:
2568 if name in ['tip', '.', 'null']:
2569 raise util.Abort(_("the name '%s' is reserved") % name)
2569 raise util.Abort(_("the name '%s' is reserved") % name)
2570 if rev_ is not None:
2570 if rev_ is not None:
2571 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2571 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2572 "please use 'hg tag [-r REV] NAME' instead\n"))
2572 "please use 'hg tag [-r REV] NAME' instead\n"))
2573 if opts['rev']:
2573 if opts['rev']:
2574 raise util.Abort(_("use only one form to specify the revision"))
2574 raise util.Abort(_("use only one form to specify the revision"))
2575 if opts['rev'] and opts['remove']:
2575 if opts['rev'] and opts['remove']:
2576 raise util.Abort(_("--rev and --remove are incompatible"))
2576 raise util.Abort(_("--rev and --remove are incompatible"))
2577 if opts['rev']:
2577 if opts['rev']:
2578 rev_ = opts['rev']
2578 rev_ = opts['rev']
2579 message = opts['message']
2579 message = opts['message']
2580 if opts['remove']:
2580 if opts['remove']:
2581 if not name in repo.tags():
2581 if not name in repo.tags():
2582 raise util.Abort(_('tag %s does not exist') % name)
2582 raise util.Abort(_('tag %s does not exist') % name)
2583 rev_ = nullid
2583 rev_ = nullid
2584 if not message:
2584 if not message:
2585 message = _('Removed tag %s') % name
2585 message = _('Removed tag %s') % name
2586 elif name in repo.tags() and not opts['force']:
2586 elif name in repo.tags() and not opts['force']:
2587 raise util.Abort(_('a tag named %s already exists (use -f to force)')
2587 raise util.Abort(_('a tag named %s already exists (use -f to force)')
2588 % name)
2588 % name)
2589 if not rev_ and repo.dirstate.parents()[1] != nullid:
2589 if not rev_ and repo.dirstate.parents()[1] != nullid:
2590 raise util.Abort(_('uncommitted merge - please provide a '
2590 raise util.Abort(_('uncommitted merge - please provide a '
2591 'specific revision'))
2591 'specific revision'))
2592 r = repo.changectx(rev_).node()
2592 r = repo.changectx(rev_).node()
2593
2593
2594 if not message:
2594 if not message:
2595 message = _('Added tag %s for changeset %s') % (name, short(r))
2595 message = _('Added tag %s for changeset %s') % (name, short(r))
2596
2596
2597 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2597 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2598
2598
2599 def tags(ui, repo):
2599 def tags(ui, repo):
2600 """list repository tags
2600 """list repository tags
2601
2601
2602 List the repository tags.
2602 List the repository tags.
2603
2603
2604 This lists both regular and local tags.
2604 This lists both regular and local tags.
2605 """
2605 """
2606
2606
2607 l = repo.tagslist()
2607 l = repo.tagslist()
2608 l.reverse()
2608 l.reverse()
2609 hexfunc = ui.debugflag and hex or short
2609 hexfunc = ui.debugflag and hex or short
2610 for t, n in l:
2610 for t, n in l:
2611 try:
2611 try:
2612 hn = hexfunc(n)
2612 hn = hexfunc(n)
2613 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2613 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2614 except revlog.LookupError:
2614 except revlog.LookupError:
2615 r = " ?:%s" % hn
2615 r = " ?:%s" % hn
2616 if ui.quiet:
2616 if ui.quiet:
2617 ui.write("%s\n" % t)
2617 ui.write("%s\n" % t)
2618 else:
2618 else:
2619 spaces = " " * (30 - util.locallen(t))
2619 spaces = " " * (30 - util.locallen(t))
2620 ui.write("%s%s %s\n" % (t, spaces, r))
2620 ui.write("%s%s %s\n" % (t, spaces, r))
2621
2621
2622 def tip(ui, repo, **opts):
2622 def tip(ui, repo, **opts):
2623 """show the tip revision
2623 """show the tip revision
2624
2624
2625 Show the tip revision.
2625 Show the tip revision.
2626 """
2626 """
2627 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2627 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2628
2628
2629 def unbundle(ui, repo, fname1, *fnames, **opts):
2629 def unbundle(ui, repo, fname1, *fnames, **opts):
2630 """apply one or more changegroup files
2630 """apply one or more changegroup files
2631
2631
2632 Apply one or more compressed changegroup files generated by the
2632 Apply one or more compressed changegroup files generated by the
2633 bundle command.
2633 bundle command.
2634 """
2634 """
2635 fnames = (fname1,) + fnames
2635 fnames = (fname1,) + fnames
2636 result = None
2636 result = None
2637 for fname in fnames:
2637 for fname in fnames:
2638 if os.path.exists(fname):
2638 if os.path.exists(fname):
2639 f = open(fname, "rb")
2639 f = open(fname, "rb")
2640 else:
2640 else:
2641 f = urllib.urlopen(fname)
2641 f = urllib.urlopen(fname)
2642 gen = changegroup.readbundle(f, fname)
2642 gen = changegroup.readbundle(f, fname)
2643 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2643 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2644
2644
2645 return postincoming(ui, repo, modheads, opts['update'])
2645 return postincoming(ui, repo, modheads, opts['update'])
2646
2646
2647 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2647 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2648 """update working directory
2648 """update working directory
2649
2649
2650 Update the working directory to the specified revision, or the
2650 Update the working directory to the specified revision, or the
2651 tip of the current branch if none is specified.
2651 tip of the current branch if none is specified.
2652
2652
2653 If there are no outstanding changes in the working directory and
2653 If there are no outstanding changes in the working directory and
2654 there is a linear relationship between the current version and the
2654 there is a linear relationship between the current version and the
2655 requested version, the result is the requested version.
2655 requested version, the result is the requested version.
2656
2656
2657 To merge the working directory with another revision, use the
2657 To merge the working directory with another revision, use the
2658 merge command.
2658 merge command.
2659
2659
2660 By default, update will refuse to run if doing so would require
2660 By default, update will refuse to run if doing so would require
2661 discarding local changes.
2661 discarding local changes.
2662 """
2662 """
2663 if rev and node:
2663 if rev and node:
2664 raise util.Abort(_("please specify just one revision"))
2664 raise util.Abort(_("please specify just one revision"))
2665
2665
2666 if not rev:
2666 if not rev:
2667 rev = node
2667 rev = node
2668
2668
2669 if date:
2669 if date:
2670 if rev:
2670 if rev:
2671 raise util.Abort(_("you can't specify a revision and a date"))
2671 raise util.Abort(_("you can't specify a revision and a date"))
2672 rev = cmdutil.finddate(ui, repo, date)
2672 rev = cmdutil.finddate(ui, repo, date)
2673
2673
2674 if clean:
2674 if clean:
2675 return hg.clean(repo, rev)
2675 return hg.clean(repo, rev)
2676 else:
2676 else:
2677 return hg.update(repo, rev)
2677 return hg.update(repo, rev)
2678
2678
2679 def verify(ui, repo):
2679 def verify(ui, repo):
2680 """verify the integrity of the repository
2680 """verify the integrity of the repository
2681
2681
2682 Verify the integrity of the current repository.
2682 Verify the integrity of the current repository.
2683
2683
2684 This will perform an extensive check of the repository's
2684 This will perform an extensive check of the repository's
2685 integrity, validating the hashes and checksums of each entry in
2685 integrity, validating the hashes and checksums of each entry in
2686 the changelog, manifest, and tracked files, as well as the
2686 the changelog, manifest, and tracked files, as well as the
2687 integrity of their crosslinks and indices.
2687 integrity of their crosslinks and indices.
2688 """
2688 """
2689 return hg.verify(repo)
2689 return hg.verify(repo)
2690
2690
2691 def version_(ui):
2691 def version_(ui):
2692 """output version and copyright information"""
2692 """output version and copyright information"""
2693 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2693 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2694 % version.get_version())
2694 % version.get_version())
2695 ui.status(_(
2695 ui.status(_(
2696 "\nCopyright (C) 2005-2007 Matt Mackall <mpm@selenic.com> and others\n"
2696 "\nCopyright (C) 2005-2007 Matt Mackall <mpm@selenic.com> and others\n"
2697 "This is free software; see the source for copying conditions. "
2697 "This is free software; see the source for copying conditions. "
2698 "There is NO\nwarranty; "
2698 "There is NO\nwarranty; "
2699 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2699 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2700 ))
2700 ))
2701
2701
2702 # Command options and aliases are listed here, alphabetically
2702 # Command options and aliases are listed here, alphabetically
2703
2703
2704 globalopts = [
2704 globalopts = [
2705 ('R', 'repository', '',
2705 ('R', 'repository', '',
2706 _('repository root directory or symbolic path name')),
2706 _('repository root directory or symbolic path name')),
2707 ('', 'cwd', '', _('change working directory')),
2707 ('', 'cwd', '', _('change working directory')),
2708 ('y', 'noninteractive', None,
2708 ('y', 'noninteractive', None,
2709 _('do not prompt, assume \'yes\' for any required answers')),
2709 _('do not prompt, assume \'yes\' for any required answers')),
2710 ('q', 'quiet', None, _('suppress output')),
2710 ('q', 'quiet', None, _('suppress output')),
2711 ('v', 'verbose', None, _('enable additional output')),
2711 ('v', 'verbose', None, _('enable additional output')),
2712 ('', 'config', [], _('set/override config option')),
2712 ('', 'config', [], _('set/override config option')),
2713 ('', 'debug', None, _('enable debugging output')),
2713 ('', 'debug', None, _('enable debugging output')),
2714 ('', 'debugger', None, _('start debugger')),
2714 ('', 'debugger', None, _('start debugger')),
2715 ('', 'encoding', util._encoding, _('set the charset encoding')),
2715 ('', 'encoding', util._encoding, _('set the charset encoding')),
2716 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2716 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2717 ('', 'lsprof', None, _('print improved command execution profile')),
2717 ('', 'lsprof', None, _('print improved command execution profile')),
2718 ('', 'traceback', None, _('print traceback on exception')),
2718 ('', 'traceback', None, _('print traceback on exception')),
2719 ('', 'time', None, _('time how long the command takes')),
2719 ('', 'time', None, _('time how long the command takes')),
2720 ('', 'profile', None, _('print command execution profile')),
2720 ('', 'profile', None, _('print command execution profile')),
2721 ('', 'version', None, _('output version information and exit')),
2721 ('', 'version', None, _('output version information and exit')),
2722 ('h', 'help', None, _('display help and exit')),
2722 ('h', 'help', None, _('display help and exit')),
2723 ]
2723 ]
2724
2724
2725 dryrunopts = [('n', 'dry-run', None,
2725 dryrunopts = [('n', 'dry-run', None,
2726 _('do not perform actions, just print output'))]
2726 _('do not perform actions, just print output'))]
2727
2727
2728 remoteopts = [
2728 remoteopts = [
2729 ('e', 'ssh', '', _('specify ssh command to use')),
2729 ('e', 'ssh', '', _('specify ssh command to use')),
2730 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2730 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2731 ]
2731 ]
2732
2732
2733 walkopts = [
2733 walkopts = [
2734 ('I', 'include', [], _('include names matching the given patterns')),
2734 ('I', 'include', [], _('include names matching the given patterns')),
2735 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2735 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2736 ]
2736 ]
2737
2737
2738 commitopts = [
2738 commitopts = [
2739 ('m', 'message', '', _('use <text> as commit message')),
2739 ('m', 'message', '', _('use <text> as commit message')),
2740 ('l', 'logfile', '', _('read commit message from <file>')),
2740 ('l', 'logfile', '', _('read commit message from <file>')),
2741 ]
2741 ]
2742
2742
2743 table = {
2743 table = {
2744 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2744 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2745 "addremove":
2745 "addremove":
2746 (addremove,
2746 (addremove,
2747 [('s', 'similarity', '',
2747 [('s', 'similarity', '',
2748 _('guess renamed files by similarity (0<=s<=100)')),
2748 _('guess renamed files by similarity (0<=s<=100)')),
2749 ] + walkopts + dryrunopts,
2749 ] + walkopts + dryrunopts,
2750 _('hg addremove [OPTION]... [FILE]...')),
2750 _('hg addremove [OPTION]... [FILE]...')),
2751 "^annotate":
2751 "^annotate":
2752 (annotate,
2752 (annotate,
2753 [('r', 'rev', '', _('annotate the specified revision')),
2753 [('r', 'rev', '', _('annotate the specified revision')),
2754 ('f', 'follow', None, _('follow file copies and renames')),
2754 ('f', 'follow', None, _('follow file copies and renames')),
2755 ('a', 'text', None, _('treat all files as text')),
2755 ('a', 'text', None, _('treat all files as text')),
2756 ('u', 'user', None, _('list the author')),
2756 ('u', 'user', None, _('list the author')),
2757 ('d', 'date', None, _('list the date')),
2757 ('d', 'date', None, _('list the date')),
2758 ('n', 'number', None, _('list the revision number (default)')),
2758 ('n', 'number', None, _('list the revision number (default)')),
2759 ('c', 'changeset', None, _('list the changeset')),
2759 ('c', 'changeset', None, _('list the changeset')),
2760 ] + walkopts,
2760 ] + walkopts,
2761 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2761 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2762 "archive":
2762 "archive":
2763 (archive,
2763 (archive,
2764 [('', 'no-decode', None, _('do not pass files through decoders')),
2764 [('', 'no-decode', None, _('do not pass files through decoders')),
2765 ('p', 'prefix', '', _('directory prefix for files in archive')),
2765 ('p', 'prefix', '', _('directory prefix for files in archive')),
2766 ('r', 'rev', '', _('revision to distribute')),
2766 ('r', 'rev', '', _('revision to distribute')),
2767 ('t', 'type', '', _('type of distribution to create')),
2767 ('t', 'type', '', _('type of distribution to create')),
2768 ] + walkopts,
2768 ] + walkopts,
2769 _('hg archive [OPTION]... DEST')),
2769 _('hg archive [OPTION]... DEST')),
2770 "backout":
2770 "backout":
2771 (backout,
2771 (backout,
2772 [('', 'merge', None,
2772 [('', 'merge', None,
2773 _('merge with old dirstate parent after backout')),
2773 _('merge with old dirstate parent after backout')),
2774 ('d', 'date', '', _('record datecode as commit date')),
2774 ('d', 'date', '', _('record datecode as commit date')),
2775 ('', 'parent', '', _('parent to choose when backing out merge')),
2775 ('', 'parent', '', _('parent to choose when backing out merge')),
2776 ('u', 'user', '', _('record user as committer')),
2776 ('u', 'user', '', _('record user as committer')),
2777 ('r', 'rev', '', _('revision to backout')),
2777 ('r', 'rev', '', _('revision to backout')),
2778 ] + walkopts + commitopts,
2778 ] + walkopts + commitopts,
2779 _('hg backout [OPTION]... [-r] REV')),
2779 _('hg backout [OPTION]... [-r] REV')),
2780 "branch":
2780 "branch":
2781 (branch,
2781 (branch,
2782 [('f', 'force', None,
2782 [('f', 'force', None,
2783 _('set branch name even if it shadows an existing branch'))],
2783 _('set branch name even if it shadows an existing branch'))],
2784 _('hg branch [NAME]')),
2784 _('hg branch [NAME]')),
2785 "branches":
2785 "branches":
2786 (branches,
2786 (branches,
2787 [('a', 'active', False,
2787 [('a', 'active', False,
2788 _('show only branches that have unmerged heads'))],
2788 _('show only branches that have unmerged heads'))],
2789 _('hg branches [-a]')),
2789 _('hg branches [-a]')),
2790 "bundle":
2790 "bundle":
2791 (bundle,
2791 (bundle,
2792 [('f', 'force', None,
2792 [('f', 'force', None,
2793 _('run even when remote repository is unrelated')),
2793 _('run even when remote repository is unrelated')),
2794 ('r', 'rev', [],
2794 ('r', 'rev', [],
2795 _('a changeset you would like to bundle')),
2795 _('a changeset you would like to bundle')),
2796 ('', 'base', [],
2796 ('', 'base', [],
2797 _('a base changeset to specify instead of a destination')),
2797 _('a base changeset to specify instead of a destination')),
2798 ] + remoteopts,
2798 ] + remoteopts,
2799 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2799 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2800 "cat":
2800 "cat":
2801 (cat,
2801 (cat,
2802 [('o', 'output', '', _('print output to file with formatted name')),
2802 [('o', 'output', '', _('print output to file with formatted name')),
2803 ('r', 'rev', '', _('print the given revision')),
2803 ('r', 'rev', '', _('print the given revision')),
2804 ] + walkopts,
2804 ] + walkopts,
2805 _('hg cat [OPTION]... FILE...')),
2805 _('hg cat [OPTION]... FILE...')),
2806 "^clone":
2806 "^clone":
2807 (clone,
2807 (clone,
2808 [('U', 'noupdate', None, _('do not update the new working directory')),
2808 [('U', 'noupdate', None, _('do not update the new working directory')),
2809 ('r', 'rev', [],
2809 ('r', 'rev', [],
2810 _('a changeset you would like to have after cloning')),
2810 _('a changeset you would like to have after cloning')),
2811 ('', 'pull', None, _('use pull protocol to copy metadata')),
2811 ('', 'pull', None, _('use pull protocol to copy metadata')),
2812 ('', 'uncompressed', None,
2812 ('', 'uncompressed', None,
2813 _('use uncompressed transfer (fast over LAN)')),
2813 _('use uncompressed transfer (fast over LAN)')),
2814 ] + remoteopts,
2814 ] + remoteopts,
2815 _('hg clone [OPTION]... SOURCE [DEST]')),
2815 _('hg clone [OPTION]... SOURCE [DEST]')),
2816 "^commit|ci":
2816 "^commit|ci":
2817 (commit,
2817 (commit,
2818 [('A', 'addremove', None,
2818 [('A', 'addremove', None,
2819 _('mark new/missing files as added/removed before committing')),
2819 _('mark new/missing files as added/removed before committing')),
2820 ('d', 'date', '', _('record datecode as commit date')),
2820 ('d', 'date', '', _('record datecode as commit date')),
2821 ('u', 'user', '', _('record user as commiter')),
2821 ('u', 'user', '', _('record user as commiter')),
2822 ] + walkopts + commitopts,
2822 ] + walkopts + commitopts,
2823 _('hg commit [OPTION]... [FILE]...')),
2823 _('hg commit [OPTION]... [FILE]...')),
2824 "copy|cp":
2824 "copy|cp":
2825 (copy,
2825 (copy,
2826 [('A', 'after', None, _('record a copy that has already occurred')),
2826 [('A', 'after', None, _('record a copy that has already occurred')),
2827 ('f', 'force', None,
2827 ('f', 'force', None,
2828 _('forcibly copy over an existing managed file')),
2828 _('forcibly copy over an existing managed file')),
2829 ] + walkopts + dryrunopts,
2829 ] + walkopts + dryrunopts,
2830 _('hg copy [OPTION]... [SOURCE]... DEST')),
2830 _('hg copy [OPTION]... [SOURCE]... DEST')),
2831 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2831 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2832 "debugcomplete":
2832 "debugcomplete":
2833 (debugcomplete,
2833 (debugcomplete,
2834 [('o', 'options', None, _('show the command options'))],
2834 [('o', 'options', None, _('show the command options'))],
2835 _('debugcomplete [-o] CMD')),
2835 _('debugcomplete [-o] CMD')),
2836 "debuginstall": (debuginstall, [], _('debuginstall')),
2836 "debuginstall": (debuginstall, [], _('debuginstall')),
2837 "debugrebuildstate":
2837 "debugrebuildstate":
2838 (debugrebuildstate,
2838 (debugrebuildstate,
2839 [('r', 'rev', '', _('revision to rebuild to'))],
2839 [('r', 'rev', '', _('revision to rebuild to'))],
2840 _('debugrebuildstate [-r REV] [REV]')),
2840 _('debugrebuildstate [-r REV] [REV]')),
2841 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2841 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2842 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2842 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2843 "debugstate": (debugstate, [], _('debugstate')),
2843 "debugstate": (debugstate, [], _('debugstate')),
2844 "debugdate":
2844 "debugdate":
2845 (debugdate,
2845 (debugdate,
2846 [('e', 'extended', None, _('try extended date formats'))],
2846 [('e', 'extended', None, _('try extended date formats'))],
2847 _('debugdate [-e] DATE [RANGE]')),
2847 _('debugdate [-e] DATE [RANGE]')),
2848 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2848 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2849 "debugindex": (debugindex, [], _('debugindex FILE')),
2849 "debugindex": (debugindex, [], _('debugindex FILE')),
2850 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2850 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2851 "debugrename":
2851 "debugrename":
2852 (debugrename,
2852 (debugrename,
2853 [('r', 'rev', '', _('revision to debug'))],
2853 [('r', 'rev', '', _('revision to debug'))],
2854 _('debugrename [-r REV] FILE')),
2854 _('debugrename [-r REV] FILE')),
2855 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2855 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2856 "^diff":
2856 "^diff":
2857 (diff,
2857 (diff,
2858 [('r', 'rev', [], _('revision')),
2858 [('r', 'rev', [], _('revision')),
2859 ('a', 'text', None, _('treat all files as text')),
2859 ('a', 'text', None, _('treat all files as text')),
2860 ('p', 'show-function', None,
2860 ('p', 'show-function', None,
2861 _('show which function each change is in')),
2861 _('show which function each change is in')),
2862 ('g', 'git', None, _('use git extended diff format')),
2862 ('g', 'git', None, _('use git extended diff format')),
2863 ('', 'nodates', None, _("don't include dates in diff headers")),
2863 ('', 'nodates', None, _("don't include dates in diff headers")),
2864 ('w', 'ignore-all-space', None,
2864 ('w', 'ignore-all-space', None,
2865 _('ignore white space when comparing lines')),
2865 _('ignore white space when comparing lines')),
2866 ('b', 'ignore-space-change', None,
2866 ('b', 'ignore-space-change', None,
2867 _('ignore changes in the amount of white space')),
2867 _('ignore changes in the amount of white space')),
2868 ('B', 'ignore-blank-lines', None,
2868 ('B', 'ignore-blank-lines', None,
2869 _('ignore changes whose lines are all blank')),
2869 _('ignore changes whose lines are all blank')),
2870 ] + walkopts,
2870 ] + walkopts,
2871 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2871 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2872 "^export":
2872 "^export":
2873 (export,
2873 (export,
2874 [('o', 'output', '', _('print output to file with formatted name')),
2874 [('o', 'output', '', _('print output to file with formatted name')),
2875 ('a', 'text', None, _('treat all files as text')),
2875 ('a', 'text', None, _('treat all files as text')),
2876 ('g', 'git', None, _('use git extended diff format')),
2876 ('g', 'git', None, _('use git extended diff format')),
2877 ('', 'nodates', None, _("don't include dates in diff headers")),
2877 ('', 'nodates', None, _("don't include dates in diff headers")),
2878 ('', 'switch-parent', None, _('diff against the second parent'))],
2878 ('', 'switch-parent', None, _('diff against the second parent'))],
2879 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2879 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2880 "grep":
2880 "grep":
2881 (grep,
2881 (grep,
2882 [('0', 'print0', None, _('end fields with NUL')),
2882 [('0', 'print0', None, _('end fields with NUL')),
2883 ('', 'all', None, _('print all revisions that match')),
2883 ('', 'all', None, _('print all revisions that match')),
2884 ('f', 'follow', None,
2884 ('f', 'follow', None,
2885 _('follow changeset history, or file history across copies and renames')),
2885 _('follow changeset history, or file history across copies and renames')),
2886 ('i', 'ignore-case', None, _('ignore case when matching')),
2886 ('i', 'ignore-case', None, _('ignore case when matching')),
2887 ('l', 'files-with-matches', None,
2887 ('l', 'files-with-matches', None,
2888 _('print only filenames and revs that match')),
2888 _('print only filenames and revs that match')),
2889 ('n', 'line-number', None, _('print matching line numbers')),
2889 ('n', 'line-number', None, _('print matching line numbers')),
2890 ('r', 'rev', [], _('search in given revision range')),
2890 ('r', 'rev', [], _('search in given revision range')),
2891 ('u', 'user', None, _('print user who committed change')),
2891 ('u', 'user', None, _('print user who committed change')),
2892 ] + walkopts,
2892 ] + walkopts,
2893 _('hg grep [OPTION]... PATTERN [FILE]...')),
2893 _('hg grep [OPTION]... PATTERN [FILE]...')),
2894 "heads":
2894 "heads":
2895 (heads,
2895 (heads,
2896 [('', 'style', '', _('display using template map file')),
2896 [('', 'style', '', _('display using template map file')),
2897 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2897 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2898 ('', 'template', '', _('display with template'))],
2898 ('', 'template', '', _('display with template'))],
2899 _('hg heads [-r REV] [REV]...')),
2899 _('hg heads [-r REV] [REV]...')),
2900 "help": (help_, [], _('hg help [COMMAND]')),
2900 "help": (help_, [], _('hg help [COMMAND]')),
2901 "identify|id":
2901 "identify|id":
2902 (identify,
2902 (identify,
2903 [('r', 'rev', '', _('identify the specified rev')),
2903 [('r', 'rev', '', _('identify the specified rev')),
2904 ('n', 'num', None, _('show local revision number')),
2904 ('n', 'num', None, _('show local revision number')),
2905 ('i', 'id', None, _('show global revision id')),
2905 ('i', 'id', None, _('show global revision id')),
2906 ('b', 'branch', None, _('show branch')),
2906 ('b', 'branch', None, _('show branch')),
2907 ('t', 'tags', None, _('show tags'))],
2907 ('t', 'tags', None, _('show tags'))],
2908 _('hg identify [-nibt] [-r REV] [SOURCE]')),
2908 _('hg identify [-nibt] [-r REV] [SOURCE]')),
2909 "import|patch":
2909 "import|patch":
2910 (import_,
2910 (import_,
2911 [('p', 'strip', 1,
2911 [('p', 'strip', 1,
2912 _('directory strip option for patch. This has the same\n'
2912 _('directory strip option for patch. This has the same\n'
2913 'meaning as the corresponding patch option')),
2913 'meaning as the corresponding patch option')),
2914 ('b', 'base', '', _('base path')),
2914 ('b', 'base', '', _('base path')),
2915 ('f', 'force', None,
2915 ('f', 'force', None,
2916 _('skip check for outstanding uncommitted changes')),
2916 _('skip check for outstanding uncommitted changes')),
2917 ('', 'exact', None,
2917 ('', 'exact', None,
2918 _('apply patch to the nodes from which it was generated')),
2918 _('apply patch to the nodes from which it was generated')),
2919 ('', 'import-branch', None,
2919 ('', 'import-branch', None,
2920 _('Use any branch information in patch (implied by --exact)'))] + commitopts,
2920 _('Use any branch information in patch (implied by --exact)'))] + commitopts,
2921 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2921 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2922 "incoming|in": (incoming,
2922 "incoming|in": (incoming,
2923 [('M', 'no-merges', None, _('do not show merges')),
2923 [('M', 'no-merges', None, _('do not show merges')),
2924 ('f', 'force', None,
2924 ('f', 'force', None,
2925 _('run even when remote repository is unrelated')),
2925 _('run even when remote repository is unrelated')),
2926 ('', 'style', '', _('display using template map file')),
2926 ('', 'style', '', _('display using template map file')),
2927 ('n', 'newest-first', None, _('show newest record first')),
2927 ('n', 'newest-first', None, _('show newest record first')),
2928 ('', 'bundle', '', _('file to store the bundles into')),
2928 ('', 'bundle', '', _('file to store the bundles into')),
2929 ('p', 'patch', None, _('show patch')),
2929 ('p', 'patch', None, _('show patch')),
2930 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2930 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2931 ('', 'template', '', _('display with template')),
2931 ('', 'template', '', _('display with template')),
2932 ] + remoteopts,
2932 ] + remoteopts,
2933 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2933 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2934 ' [--bundle FILENAME] [SOURCE]')),
2934 ' [--bundle FILENAME] [SOURCE]')),
2935 "^init":
2935 "^init":
2936 (init,
2936 (init,
2937 remoteopts,
2937 remoteopts,
2938 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2938 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2939 "locate":
2939 "locate":
2940 (locate,
2940 (locate,
2941 [('r', 'rev', '', _('search the repository as it stood at rev')),
2941 [('r', 'rev', '', _('search the repository as it stood at rev')),
2942 ('0', 'print0', None,
2942 ('0', 'print0', None,
2943 _('end filenames with NUL, for use with xargs')),
2943 _('end filenames with NUL, for use with xargs')),
2944 ('f', 'fullpath', None,
2944 ('f', 'fullpath', None,
2945 _('print complete paths from the filesystem root')),
2945 _('print complete paths from the filesystem root')),
2946 ] + walkopts,
2946 ] + walkopts,
2947 _('hg locate [OPTION]... [PATTERN]...')),
2947 _('hg locate [OPTION]... [PATTERN]...')),
2948 "^log|history":
2948 "^log|history":
2949 (log,
2949 (log,
2950 [('f', 'follow', None,
2950 [('f', 'follow', None,
2951 _('follow changeset history, or file history across copies and renames')),
2951 _('follow changeset history, or file history across copies and renames')),
2952 ('', 'follow-first', None,
2952 ('', 'follow-first', None,
2953 _('only follow the first parent of merge changesets')),
2953 _('only follow the first parent of merge changesets')),
2954 ('d', 'date', '', _('show revs matching date spec')),
2954 ('d', 'date', '', _('show revs matching date spec')),
2955 ('C', 'copies', None, _('show copied files')),
2955 ('C', 'copies', None, _('show copied files')),
2956 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
2956 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
2957 ('l', 'limit', '', _('limit number of changes displayed')),
2957 ('l', 'limit', '', _('limit number of changes displayed')),
2958 ('r', 'rev', [], _('show the specified revision or range')),
2958 ('r', 'rev', [], _('show the specified revision or range')),
2959 ('', 'removed', None, _('include revs where files were removed')),
2959 ('', 'removed', None, _('include revs where files were removed')),
2960 ('M', 'no-merges', None, _('do not show merges')),
2960 ('M', 'no-merges', None, _('do not show merges')),
2961 ('', 'style', '', _('display using template map file')),
2961 ('', 'style', '', _('display using template map file')),
2962 ('m', 'only-merges', None, _('show only merges')),
2962 ('m', 'only-merges', None, _('show only merges')),
2963 ('p', 'patch', None, _('show patch')),
2963 ('p', 'patch', None, _('show patch')),
2964 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2964 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2965 ('', 'template', '', _('display with template')),
2965 ('', 'template', '', _('display with template')),
2966 ] + walkopts,
2966 ] + walkopts,
2967 _('hg log [OPTION]... [FILE]')),
2967 _('hg log [OPTION]... [FILE]')),
2968 "manifest": (manifest, [], _('hg manifest [REV]')),
2968 "manifest": (manifest, [], _('hg manifest [REV]')),
2969 "^merge":
2969 "^merge":
2970 (merge,
2970 (merge,
2971 [('f', 'force', None, _('force a merge with outstanding changes')),
2971 [('f', 'force', None, _('force a merge with outstanding changes')),
2972 ('r', 'rev', '', _('revision to merge')),
2972 ('r', 'rev', '', _('revision to merge')),
2973 ],
2973 ],
2974 _('hg merge [-f] [[-r] REV]')),
2974 _('hg merge [-f] [[-r] REV]')),
2975 "outgoing|out": (outgoing,
2975 "outgoing|out": (outgoing,
2976 [('M', 'no-merges', None, _('do not show merges')),
2976 [('M', 'no-merges', None, _('do not show merges')),
2977 ('f', 'force', None,
2977 ('f', 'force', None,
2978 _('run even when remote repository is unrelated')),
2978 _('run even when remote repository is unrelated')),
2979 ('p', 'patch', None, _('show patch')),
2979 ('p', 'patch', None, _('show patch')),
2980 ('', 'style', '', _('display using template map file')),
2980 ('', 'style', '', _('display using template map file')),
2981 ('r', 'rev', [], _('a specific revision you would like to push')),
2981 ('r', 'rev', [], _('a specific revision you would like to push')),
2982 ('n', 'newest-first', None, _('show newest record first')),
2982 ('n', 'newest-first', None, _('show newest record first')),
2983 ('', 'template', '', _('display with template')),
2983 ('', 'template', '', _('display with template')),
2984 ] + remoteopts,
2984 ] + remoteopts,
2985 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2985 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2986 "^parents":
2986 "^parents":
2987 (parents,
2987 (parents,
2988 [('r', 'rev', '', _('show parents from the specified rev')),
2988 [('r', 'rev', '', _('show parents from the specified rev')),
2989 ('', 'style', '', _('display using template map file')),
2989 ('', 'style', '', _('display using template map file')),
2990 ('', 'template', '', _('display with template'))],
2990 ('', 'template', '', _('display with template'))],
2991 _('hg parents [-r REV] [FILE]')),
2991 _('hg parents [-r REV] [FILE]')),
2992 "paths": (paths, [], _('hg paths [NAME]')),
2992 "paths": (paths, [], _('hg paths [NAME]')),
2993 "^pull":
2993 "^pull":
2994 (pull,
2994 (pull,
2995 [('u', 'update', None,
2995 [('u', 'update', None,
2996 _('update to new tip if changesets were pulled')),
2996 _('update to new tip if changesets were pulled')),
2997 ('f', 'force', None,
2997 ('f', 'force', None,
2998 _('run even when remote repository is unrelated')),
2998 _('run even when remote repository is unrelated')),
2999 ('r', 'rev', [],
2999 ('r', 'rev', [],
3000 _('a specific revision up to which you would like to pull')),
3000 _('a specific revision up to which you would like to pull')),
3001 ] + remoteopts,
3001 ] + remoteopts,
3002 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3002 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3003 "^push":
3003 "^push":
3004 (push,
3004 (push,
3005 [('f', 'force', None, _('force push')),
3005 [('f', 'force', None, _('force push')),
3006 ('r', 'rev', [], _('a specific revision you would like to push')),
3006 ('r', 'rev', [], _('a specific revision you would like to push')),
3007 ] + remoteopts,
3007 ] + remoteopts,
3008 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3008 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3009 "debugrawcommit|rawcommit":
3009 "debugrawcommit|rawcommit":
3010 (rawcommit,
3010 (rawcommit,
3011 [('p', 'parent', [], _('parent')),
3011 [('p', 'parent', [], _('parent')),
3012 ('d', 'date', '', _('date code')),
3012 ('d', 'date', '', _('date code')),
3013 ('u', 'user', '', _('user')),
3013 ('u', 'user', '', _('user')),
3014 ('F', 'files', '', _('file list'))
3014 ('F', 'files', '', _('file list'))
3015 ] + commitopts,
3015 ] + commitopts,
3016 _('hg debugrawcommit [OPTION]... [FILE]...')),
3016 _('hg debugrawcommit [OPTION]... [FILE]...')),
3017 "recover": (recover, [], _('hg recover')),
3017 "recover": (recover, [], _('hg recover')),
3018 "^remove|rm":
3018 "^remove|rm":
3019 (remove,
3019 (remove,
3020 [('A', 'after', None, _('record remove that has already occurred')),
3020 [('A', 'after', None, _('record remove that has already occurred')),
3021 ('f', 'force', None, _('remove file even if modified')),
3021 ('f', 'force', None, _('remove file even if modified')),
3022 ] + walkopts,
3022 ] + walkopts,
3023 _('hg remove [OPTION]... FILE...')),
3023 _('hg remove [OPTION]... FILE...')),
3024 "rename|mv":
3024 "rename|mv":
3025 (rename,
3025 (rename,
3026 [('A', 'after', None, _('record a rename that has already occurred')),
3026 [('A', 'after', None, _('record a rename that has already occurred')),
3027 ('f', 'force', None,
3027 ('f', 'force', None,
3028 _('forcibly copy over an existing managed file')),
3028 _('forcibly copy over an existing managed file')),
3029 ] + walkopts + dryrunopts,
3029 ] + walkopts + dryrunopts,
3030 _('hg rename [OPTION]... SOURCE... DEST')),
3030 _('hg rename [OPTION]... SOURCE... DEST')),
3031 "^revert":
3031 "^revert":
3032 (revert,
3032 (revert,
3033 [('a', 'all', None, _('revert all changes when no arguments given')),
3033 [('a', 'all', None, _('revert all changes when no arguments given')),
3034 ('d', 'date', '', _('tipmost revision matching date')),
3034 ('d', 'date', '', _('tipmost revision matching date')),
3035 ('r', 'rev', '', _('revision to revert to')),
3035 ('r', 'rev', '', _('revision to revert to')),
3036 ('', 'no-backup', None, _('do not save backup copies of files')),
3036 ('', 'no-backup', None, _('do not save backup copies of files')),
3037 ] + walkopts + dryrunopts,
3037 ] + walkopts + dryrunopts,
3038 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3038 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3039 "rollback": (rollback, [], _('hg rollback')),
3039 "rollback": (rollback, [], _('hg rollback')),
3040 "root": (root, [], _('hg root')),
3040 "root": (root, [], _('hg root')),
3041 "showconfig|debugconfig":
3041 "showconfig|debugconfig":
3042 (showconfig,
3042 (showconfig,
3043 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3043 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3044 _('showconfig [-u] [NAME]...')),
3044 _('showconfig [-u] [NAME]...')),
3045 "^serve":
3045 "^serve":
3046 (serve,
3046 (serve,
3047 [('A', 'accesslog', '', _('name of access log file to write to')),
3047 [('A', 'accesslog', '', _('name of access log file to write to')),
3048 ('d', 'daemon', None, _('run server in background')),
3048 ('d', 'daemon', None, _('run server in background')),
3049 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3049 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3050 ('E', 'errorlog', '', _('name of error log file to write to')),
3050 ('E', 'errorlog', '', _('name of error log file to write to')),
3051 ('p', 'port', 0, _('port to use (default: 8000)')),
3051 ('p', 'port', 0, _('port to use (default: 8000)')),
3052 ('a', 'address', '', _('address to use')),
3052 ('a', 'address', '', _('address to use')),
3053 ('n', 'name', '',
3053 ('n', 'name', '',
3054 _('name to show in web pages (default: working dir)')),
3054 _('name to show in web pages (default: working dir)')),
3055 ('', 'webdir-conf', '', _('name of the webdir config file'
3055 ('', 'webdir-conf', '', _('name of the webdir config file'
3056 ' (serve more than one repo)')),
3056 ' (serve more than one repo)')),
3057 ('', 'pid-file', '', _('name of file to write process ID to')),
3057 ('', 'pid-file', '', _('name of file to write process ID to')),
3058 ('', 'stdio', None, _('for remote clients')),
3058 ('', 'stdio', None, _('for remote clients')),
3059 ('t', 'templates', '', _('web templates to use')),
3059 ('t', 'templates', '', _('web templates to use')),
3060 ('', 'style', '', _('template style to use')),
3060 ('', 'style', '', _('template style to use')),
3061 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3061 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3062 _('hg serve [OPTION]...')),
3062 _('hg serve [OPTION]...')),
3063 "^status|st":
3063 "^status|st":
3064 (status,
3064 (status,
3065 [('A', 'all', None, _('show status of all files')),
3065 [('A', 'all', None, _('show status of all files')),
3066 ('m', 'modified', None, _('show only modified files')),
3066 ('m', 'modified', None, _('show only modified files')),
3067 ('a', 'added', None, _('show only added files')),
3067 ('a', 'added', None, _('show only added files')),
3068 ('r', 'removed', None, _('show only removed files')),
3068 ('r', 'removed', None, _('show only removed files')),
3069 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3069 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3070 ('c', 'clean', None, _('show only files without changes')),
3070 ('c', 'clean', None, _('show only files without changes')),
3071 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3071 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3072 ('i', 'ignored', None, _('show only ignored files')),
3072 ('i', 'ignored', None, _('show only ignored files')),
3073 ('n', 'no-status', None, _('hide status prefix')),
3073 ('n', 'no-status', None, _('hide status prefix')),
3074 ('C', 'copies', None, _('show source of copied files')),
3074 ('C', 'copies', None, _('show source of copied files')),
3075 ('0', 'print0', None,
3075 ('0', 'print0', None,
3076 _('end filenames with NUL, for use with xargs')),
3076 _('end filenames with NUL, for use with xargs')),
3077 ('', 'rev', [], _('show difference from revision')),
3077 ('', 'rev', [], _('show difference from revision')),
3078 ] + walkopts,
3078 ] + walkopts,
3079 _('hg status [OPTION]... [FILE]...')),
3079 _('hg status [OPTION]... [FILE]...')),
3080 "tag":
3080 "tag":
3081 (tag,
3081 (tag,
3082 [('f', 'force', None, _('replace existing tag')),
3082 [('f', 'force', None, _('replace existing tag')),
3083 ('l', 'local', None, _('make the tag local')),
3083 ('l', 'local', None, _('make the tag local')),
3084 ('m', 'message', '', _('message for tag commit log entry')),
3084 ('m', 'message', '', _('message for tag commit log entry')),
3085 ('d', 'date', '', _('record datecode as commit date')),
3085 ('d', 'date', '', _('record datecode as commit date')),
3086 ('u', 'user', '', _('record user as commiter')),
3086 ('u', 'user', '', _('record user as commiter')),
3087 ('r', 'rev', '', _('revision to tag')),
3087 ('r', 'rev', '', _('revision to tag')),
3088 ('', 'remove', None, _('remove a tag'))],
3088 ('', 'remove', None, _('remove a tag'))],
3089 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3089 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3090 "tags": (tags, [], _('hg tags')),
3090 "tags": (tags, [], _('hg tags')),
3091 "tip":
3091 "tip":
3092 (tip,
3092 (tip,
3093 [('', 'style', '', _('display using template map file')),
3093 [('', 'style', '', _('display using template map file')),
3094 ('p', 'patch', None, _('show patch')),
3094 ('p', 'patch', None, _('show patch')),
3095 ('', 'template', '', _('display with template'))],
3095 ('', 'template', '', _('display with template'))],
3096 _('hg tip [-p]')),
3096 _('hg tip [-p]')),
3097 "unbundle":
3097 "unbundle":
3098 (unbundle,
3098 (unbundle,
3099 [('u', 'update', None,
3099 [('u', 'update', None,
3100 _('update to new tip if changesets were unbundled'))],
3100 _('update to new tip if changesets were unbundled'))],
3101 _('hg unbundle [-u] FILE...')),
3101 _('hg unbundle [-u] FILE...')),
3102 "^update|up|checkout|co":
3102 "^update|up|checkout|co":
3103 (update,
3103 (update,
3104 [('C', 'clean', None, _('overwrite locally modified files')),
3104 [('C', 'clean', None, _('overwrite locally modified files')),
3105 ('d', 'date', '', _('tipmost revision matching date')),
3105 ('d', 'date', '', _('tipmost revision matching date')),
3106 ('r', 'rev', '', _('revision'))],
3106 ('r', 'rev', '', _('revision'))],
3107 _('hg update [-C] [-d DATE] [[-r] REV]')),
3107 _('hg update [-C] [-d DATE] [[-r] REV]')),
3108 "verify": (verify, [], _('hg verify')),
3108 "verify": (verify, [], _('hg verify')),
3109 "version": (version_, [], _('hg version')),
3109 "version": (version_, [], _('hg version')),
3110 }
3110 }
3111
3111
3112 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3112 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3113 " debugindex debugindexdot debugdate debuginstall")
3113 " debugindex debugindexdot debugdate debuginstall")
3114 optionalrepo = ("paths serve showconfig")
3114 optionalrepo = ("paths serve showconfig")
3115
3115
3116 def dispatch(args, argv0=None):
3116 def dispatch(args, argv0=None):
3117 try:
3117 try:
3118 u = ui.ui(traceback='--traceback' in args)
3118 u = ui.ui(traceback='--traceback' in args)
3119 except util.Abort, inst:
3119 except util.Abort, inst:
3120 sys.stderr.write(_("abort: %s\n") % inst)
3120 sys.stderr.write(_("abort: %s\n") % inst)
3121 return -1
3121 return -1
3122 return cmdutil.runcatch(u, args, argv0=argv0)
3122 return cmdutil.runcatch(u, args, argv0=argv0)
3123
3123
3124 def run():
3124 def run():
3125 sys.exit(dispatch(sys.argv[1:], argv0=sys.argv[0]))
3125 sys.exit(dispatch(sys.argv[1:], argv0=sys.argv[0]))
@@ -1,1564 +1,1570
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
16 import cStringIO, errno, getpass, popen2, re, shutil, sys, tempfile
17 import os, threading, time, calendar, ConfigParser, locale, glob
17 import os, threading, time, calendar, ConfigParser, locale, glob
18
18
19 try:
19 try:
20 set = set
20 set = set
21 frozenset = frozenset
21 frozenset = frozenset
22 except NameError:
22 except NameError:
23 from sets import Set as set, ImmutableSet as frozenset
23 from sets import Set as set, ImmutableSet as frozenset
24
24
25 try:
25 try:
26 _encoding = os.environ.get("HGENCODING")
26 _encoding = os.environ.get("HGENCODING")
27 if sys.platform == 'darwin' and not _encoding:
27 if sys.platform == 'darwin' and not _encoding:
28 # On darwin, getpreferredencoding ignores the locale environment and
28 # On darwin, getpreferredencoding ignores the locale environment and
29 # always returns mac-roman. We override this if the environment is
29 # always returns mac-roman. We override this if the environment is
30 # not C (has been customized by the user).
30 # not C (has been customized by the user).
31 locale.setlocale(locale.LC_CTYPE, '')
31 locale.setlocale(locale.LC_CTYPE, '')
32 _encoding = locale.getlocale()[1]
32 _encoding = locale.getlocale()[1]
33 if not _encoding:
33 if not _encoding:
34 _encoding = locale.getpreferredencoding() or 'ascii'
34 _encoding = locale.getpreferredencoding() or 'ascii'
35 except locale.Error:
35 except locale.Error:
36 _encoding = 'ascii'
36 _encoding = 'ascii'
37 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
37 _encodingmode = os.environ.get("HGENCODINGMODE", "strict")
38 _fallbackencoding = 'ISO-8859-1'
38 _fallbackencoding = 'ISO-8859-1'
39
39
40 def tolocal(s):
40 def tolocal(s):
41 """
41 """
42 Convert a string from internal UTF-8 to local encoding
42 Convert a string from internal UTF-8 to local encoding
43
43
44 All internal strings should be UTF-8 but some repos before the
44 All internal strings should be UTF-8 but some repos before the
45 implementation of locale support may contain latin1 or possibly
45 implementation of locale support may contain latin1 or possibly
46 other character sets. We attempt to decode everything strictly
46 other character sets. We attempt to decode everything strictly
47 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
47 using UTF-8, then Latin-1, and failing that, we use UTF-8 and
48 replace unknown characters.
48 replace unknown characters.
49 """
49 """
50 for e in ('UTF-8', _fallbackencoding):
50 for e in ('UTF-8', _fallbackencoding):
51 try:
51 try:
52 u = s.decode(e) # attempt strict decoding
52 u = s.decode(e) # attempt strict decoding
53 return u.encode(_encoding, "replace")
53 return u.encode(_encoding, "replace")
54 except LookupError, k:
54 except LookupError, k:
55 raise Abort(_("%s, please check your locale settings") % k)
55 raise Abort(_("%s, please check your locale settings") % k)
56 except UnicodeDecodeError:
56 except UnicodeDecodeError:
57 pass
57 pass
58 u = s.decode("utf-8", "replace") # last ditch
58 u = s.decode("utf-8", "replace") # last ditch
59 return u.encode(_encoding, "replace")
59 return u.encode(_encoding, "replace")
60
60
61 def fromlocal(s):
61 def fromlocal(s):
62 """
62 """
63 Convert a string from the local character encoding to UTF-8
63 Convert a string from the local character encoding to UTF-8
64
64
65 We attempt to decode strings using the encoding mode set by
65 We attempt to decode strings using the encoding mode set by
66 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
66 HG_ENCODINGMODE, which defaults to 'strict'. In this mode, unknown
67 characters will cause an error message. Other modes include
67 characters will cause an error message. Other modes include
68 'replace', which replaces unknown characters with a special
68 'replace', which replaces unknown characters with a special
69 Unicode character, and 'ignore', which drops the character.
69 Unicode character, and 'ignore', which drops the character.
70 """
70 """
71 try:
71 try:
72 return s.decode(_encoding, _encodingmode).encode("utf-8")
72 return s.decode(_encoding, _encodingmode).encode("utf-8")
73 except UnicodeDecodeError, inst:
73 except UnicodeDecodeError, inst:
74 sub = s[max(0, inst.start-10):inst.start+10]
74 sub = s[max(0, inst.start-10):inst.start+10]
75 raise Abort("decoding near '%s': %s!" % (sub, inst))
75 raise Abort("decoding near '%s': %s!" % (sub, inst))
76 except LookupError, k:
76 except LookupError, k:
77 raise Abort(_("%s, please check your locale settings") % k)
77 raise Abort(_("%s, please check your locale settings") % k)
78
78
79 def locallen(s):
79 def locallen(s):
80 """Find the length in characters of a local string"""
80 """Find the length in characters of a local string"""
81 return len(s.decode(_encoding, "replace"))
81 return len(s.decode(_encoding, "replace"))
82
82
83 def localsub(s, a, b=None):
83 def localsub(s, a, b=None):
84 try:
84 try:
85 u = s.decode(_encoding, _encodingmode)
85 u = s.decode(_encoding, _encodingmode)
86 if b is not None:
86 if b is not None:
87 u = u[a:b]
87 u = u[a:b]
88 else:
88 else:
89 u = u[:a]
89 u = u[:a]
90 return u.encode(_encoding, _encodingmode)
90 return u.encode(_encoding, _encodingmode)
91 except UnicodeDecodeError, inst:
91 except UnicodeDecodeError, inst:
92 sub = s[max(0, inst.start-10), inst.start+10]
92 sub = s[max(0, inst.start-10), inst.start+10]
93 raise Abort(_("decoding near '%s': %s!") % (sub, inst))
93 raise Abort(_("decoding near '%s': %s!") % (sub, inst))
94
94
95 # used by parsedate
95 # used by parsedate
96 defaultdateformats = (
96 defaultdateformats = (
97 '%Y-%m-%d %H:%M:%S',
97 '%Y-%m-%d %H:%M:%S',
98 '%Y-%m-%d %I:%M:%S%p',
98 '%Y-%m-%d %I:%M:%S%p',
99 '%Y-%m-%d %H:%M',
99 '%Y-%m-%d %H:%M',
100 '%Y-%m-%d %I:%M%p',
100 '%Y-%m-%d %I:%M%p',
101 '%Y-%m-%d',
101 '%Y-%m-%d',
102 '%m-%d',
102 '%m-%d',
103 '%m/%d',
103 '%m/%d',
104 '%m/%d/%y',
104 '%m/%d/%y',
105 '%m/%d/%Y',
105 '%m/%d/%Y',
106 '%a %b %d %H:%M:%S %Y',
106 '%a %b %d %H:%M:%S %Y',
107 '%a %b %d %I:%M:%S%p %Y',
107 '%a %b %d %I:%M:%S%p %Y',
108 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
108 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
109 '%b %d %H:%M:%S %Y',
109 '%b %d %H:%M:%S %Y',
110 '%b %d %I:%M:%S%p %Y',
110 '%b %d %I:%M:%S%p %Y',
111 '%b %d %H:%M:%S',
111 '%b %d %H:%M:%S',
112 '%b %d %I:%M:%S%p',
112 '%b %d %I:%M:%S%p',
113 '%b %d %H:%M',
113 '%b %d %H:%M',
114 '%b %d %I:%M%p',
114 '%b %d %I:%M%p',
115 '%b %d %Y',
115 '%b %d %Y',
116 '%b %d',
116 '%b %d',
117 '%H:%M:%S',
117 '%H:%M:%S',
118 '%I:%M:%SP',
118 '%I:%M:%SP',
119 '%H:%M',
119 '%H:%M',
120 '%I:%M%p',
120 '%I:%M%p',
121 )
121 )
122
122
123 extendeddateformats = defaultdateformats + (
123 extendeddateformats = defaultdateformats + (
124 "%Y",
124 "%Y",
125 "%Y-%m",
125 "%Y-%m",
126 "%b",
126 "%b",
127 "%b %Y",
127 "%b %Y",
128 )
128 )
129
129
130 class SignalInterrupt(Exception):
130 class SignalInterrupt(Exception):
131 """Exception raised on SIGTERM and SIGHUP."""
131 """Exception raised on SIGTERM and SIGHUP."""
132
132
133 # differences from SafeConfigParser:
133 # differences from SafeConfigParser:
134 # - case-sensitive keys
134 # - case-sensitive keys
135 # - allows values that are not strings (this means that you may not
135 # - allows values that are not strings (this means that you may not
136 # be able to save the configuration to a file)
136 # be able to save the configuration to a file)
137 class configparser(ConfigParser.SafeConfigParser):
137 class configparser(ConfigParser.SafeConfigParser):
138 def optionxform(self, optionstr):
138 def optionxform(self, optionstr):
139 return optionstr
139 return optionstr
140
140
141 def set(self, section, option, value):
141 def set(self, section, option, value):
142 return ConfigParser.ConfigParser.set(self, section, option, value)
142 return ConfigParser.ConfigParser.set(self, section, option, value)
143
143
144 def _interpolate(self, section, option, rawval, vars):
144 def _interpolate(self, section, option, rawval, vars):
145 if not isinstance(rawval, basestring):
145 if not isinstance(rawval, basestring):
146 return rawval
146 return rawval
147 return ConfigParser.SafeConfigParser._interpolate(self, section,
147 return ConfigParser.SafeConfigParser._interpolate(self, section,
148 option, rawval, vars)
148 option, rawval, vars)
149
149
150 def cachefunc(func):
150 def cachefunc(func):
151 '''cache the result of function calls'''
151 '''cache the result of function calls'''
152 # XXX doesn't handle keywords args
152 # XXX doesn't handle keywords args
153 cache = {}
153 cache = {}
154 if func.func_code.co_argcount == 1:
154 if func.func_code.co_argcount == 1:
155 # we gain a small amount of time because
155 # we gain a small amount of time because
156 # we don't need to pack/unpack the list
156 # we don't need to pack/unpack the list
157 def f(arg):
157 def f(arg):
158 if arg not in cache:
158 if arg not in cache:
159 cache[arg] = func(arg)
159 cache[arg] = func(arg)
160 return cache[arg]
160 return cache[arg]
161 else:
161 else:
162 def f(*args):
162 def f(*args):
163 if args not in cache:
163 if args not in cache:
164 cache[args] = func(*args)
164 cache[args] = func(*args)
165 return cache[args]
165 return cache[args]
166
166
167 return f
167 return f
168
168
169 def pipefilter(s, cmd):
169 def pipefilter(s, cmd):
170 '''filter string S through command CMD, returning its output'''
170 '''filter string S through command CMD, returning its output'''
171 (pin, pout) = os.popen2(cmd, 'b')
171 (pin, pout) = os.popen2(cmd, 'b')
172 def writer():
172 def writer():
173 try:
173 try:
174 pin.write(s)
174 pin.write(s)
175 pin.close()
175 pin.close()
176 except IOError, inst:
176 except IOError, inst:
177 if inst.errno != errno.EPIPE:
177 if inst.errno != errno.EPIPE:
178 raise
178 raise
179
179
180 # we should use select instead on UNIX, but this will work on most
180 # we should use select instead on UNIX, but this will work on most
181 # systems, including Windows
181 # systems, including Windows
182 w = threading.Thread(target=writer)
182 w = threading.Thread(target=writer)
183 w.start()
183 w.start()
184 f = pout.read()
184 f = pout.read()
185 pout.close()
185 pout.close()
186 w.join()
186 w.join()
187 return f
187 return f
188
188
189 def tempfilter(s, cmd):
189 def tempfilter(s, cmd):
190 '''filter string S through a pair of temporary files with CMD.
190 '''filter string S through a pair of temporary files with CMD.
191 CMD is used as a template to create the real command to be run,
191 CMD is used as a template to create the real command to be run,
192 with the strings INFILE and OUTFILE replaced by the real names of
192 with the strings INFILE and OUTFILE replaced by the real names of
193 the temporary files generated.'''
193 the temporary files generated.'''
194 inname, outname = None, None
194 inname, outname = None, None
195 try:
195 try:
196 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
196 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
197 fp = os.fdopen(infd, 'wb')
197 fp = os.fdopen(infd, 'wb')
198 fp.write(s)
198 fp.write(s)
199 fp.close()
199 fp.close()
200 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
200 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
201 os.close(outfd)
201 os.close(outfd)
202 cmd = cmd.replace('INFILE', inname)
202 cmd = cmd.replace('INFILE', inname)
203 cmd = cmd.replace('OUTFILE', outname)
203 cmd = cmd.replace('OUTFILE', outname)
204 code = os.system(cmd)
204 code = os.system(cmd)
205 if sys.platform == 'OpenVMS' and code & 1:
205 if sys.platform == 'OpenVMS' and code & 1:
206 code = 0
206 code = 0
207 if code: raise Abort(_("command '%s' failed: %s") %
207 if code: raise Abort(_("command '%s' failed: %s") %
208 (cmd, explain_exit(code)))
208 (cmd, explain_exit(code)))
209 return open(outname, 'rb').read()
209 return open(outname, 'rb').read()
210 finally:
210 finally:
211 try:
211 try:
212 if inname: os.unlink(inname)
212 if inname: os.unlink(inname)
213 except: pass
213 except: pass
214 try:
214 try:
215 if outname: os.unlink(outname)
215 if outname: os.unlink(outname)
216 except: pass
216 except: pass
217
217
218 filtertable = {
218 filtertable = {
219 'tempfile:': tempfilter,
219 'tempfile:': tempfilter,
220 'pipe:': pipefilter,
220 'pipe:': pipefilter,
221 }
221 }
222
222
223 def filter(s, cmd):
223 def filter(s, cmd):
224 "filter a string through a command that transforms its input to its output"
224 "filter a string through a command that transforms its input to its output"
225 for name, fn in filtertable.iteritems():
225 for name, fn in filtertable.iteritems():
226 if cmd.startswith(name):
226 if cmd.startswith(name):
227 return fn(s, cmd[len(name):].lstrip())
227 return fn(s, cmd[len(name):].lstrip())
228 return pipefilter(s, cmd)
228 return pipefilter(s, cmd)
229
229
230 def binary(s):
230 def binary(s):
231 """return true if a string is binary data using diff's heuristic"""
231 """return true if a string is binary data using diff's heuristic"""
232 if s and '\0' in s[:4096]:
232 if s and '\0' in s[:4096]:
233 return True
233 return True
234 return False
234 return False
235
235
236 def unique(g):
236 def unique(g):
237 """return the uniq elements of iterable g"""
237 """return the uniq elements of iterable g"""
238 seen = {}
238 seen = {}
239 l = []
239 l = []
240 for f in g:
240 for f in g:
241 if f not in seen:
241 if f not in seen:
242 seen[f] = 1
242 seen[f] = 1
243 l.append(f)
243 l.append(f)
244 return l
244 return l
245
245
246 class Abort(Exception):
246 class Abort(Exception):
247 """Raised if a command needs to print an error and exit."""
247 """Raised if a command needs to print an error and exit."""
248
248
249 class UnexpectedOutput(Abort):
249 class UnexpectedOutput(Abort):
250 """Raised to print an error with part of output and exit."""
250 """Raised to print an error with part of output and exit."""
251
251
252 def always(fn): return True
252 def always(fn): return True
253 def never(fn): return False
253 def never(fn): return False
254
254
255 def expand_glob(pats):
255 def expand_glob(pats):
256 '''On Windows, expand the implicit globs in a list of patterns'''
256 '''On Windows, expand the implicit globs in a list of patterns'''
257 if os.name != 'nt':
257 if os.name != 'nt':
258 return list(pats)
258 return list(pats)
259 ret = []
259 ret = []
260 for p in pats:
260 for p in pats:
261 kind, name = patkind(p, None)
261 kind, name = patkind(p, None)
262 if kind is None:
262 if kind is None:
263 globbed = glob.glob(name)
263 globbed = glob.glob(name)
264 if globbed:
264 if globbed:
265 ret.extend(globbed)
265 ret.extend(globbed)
266 continue
266 continue
267 # if we couldn't expand the glob, just keep it around
267 # if we couldn't expand the glob, just keep it around
268 ret.append(p)
268 ret.append(p)
269 return ret
269 return ret
270
270
271 def patkind(name, dflt_pat='glob'):
271 def patkind(name, dflt_pat='glob'):
272 """Split a string into an optional pattern kind prefix and the
272 """Split a string into an optional pattern kind prefix and the
273 actual pattern."""
273 actual pattern."""
274 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
274 for prefix in 're', 'glob', 'path', 'relglob', 'relpath', 'relre':
275 if name.startswith(prefix + ':'): return name.split(':', 1)
275 if name.startswith(prefix + ':'): return name.split(':', 1)
276 return dflt_pat, name
276 return dflt_pat, name
277
277
278 def globre(pat, head='^', tail='$'):
278 def globre(pat, head='^', tail='$'):
279 "convert a glob pattern into a regexp"
279 "convert a glob pattern into a regexp"
280 i, n = 0, len(pat)
280 i, n = 0, len(pat)
281 res = ''
281 res = ''
282 group = False
282 group = False
283 def peek(): return i < n and pat[i]
283 def peek(): return i < n and pat[i]
284 while i < n:
284 while i < n:
285 c = pat[i]
285 c = pat[i]
286 i = i+1
286 i = i+1
287 if c == '*':
287 if c == '*':
288 if peek() == '*':
288 if peek() == '*':
289 i += 1
289 i += 1
290 res += '.*'
290 res += '.*'
291 else:
291 else:
292 res += '[^/]*'
292 res += '[^/]*'
293 elif c == '?':
293 elif c == '?':
294 res += '.'
294 res += '.'
295 elif c == '[':
295 elif c == '[':
296 j = i
296 j = i
297 if j < n and pat[j] in '!]':
297 if j < n and pat[j] in '!]':
298 j += 1
298 j += 1
299 while j < n and pat[j] != ']':
299 while j < n and pat[j] != ']':
300 j += 1
300 j += 1
301 if j >= n:
301 if j >= n:
302 res += '\\['
302 res += '\\['
303 else:
303 else:
304 stuff = pat[i:j].replace('\\','\\\\')
304 stuff = pat[i:j].replace('\\','\\\\')
305 i = j + 1
305 i = j + 1
306 if stuff[0] == '!':
306 if stuff[0] == '!':
307 stuff = '^' + stuff[1:]
307 stuff = '^' + stuff[1:]
308 elif stuff[0] == '^':
308 elif stuff[0] == '^':
309 stuff = '\\' + stuff
309 stuff = '\\' + stuff
310 res = '%s[%s]' % (res, stuff)
310 res = '%s[%s]' % (res, stuff)
311 elif c == '{':
311 elif c == '{':
312 group = True
312 group = True
313 res += '(?:'
313 res += '(?:'
314 elif c == '}' and group:
314 elif c == '}' and group:
315 res += ')'
315 res += ')'
316 group = False
316 group = False
317 elif c == ',' and group:
317 elif c == ',' and group:
318 res += '|'
318 res += '|'
319 elif c == '\\':
319 elif c == '\\':
320 p = peek()
320 p = peek()
321 if p:
321 if p:
322 i += 1
322 i += 1
323 res += re.escape(p)
323 res += re.escape(p)
324 else:
324 else:
325 res += re.escape(c)
325 res += re.escape(c)
326 else:
326 else:
327 res += re.escape(c)
327 res += re.escape(c)
328 return head + res + tail
328 return head + res + tail
329
329
330 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
330 _globchars = {'[': 1, '{': 1, '*': 1, '?': 1}
331
331
332 def pathto(root, n1, n2):
332 def pathto(root, n1, n2):
333 '''return the relative path from one place to another.
333 '''return the relative path from one place to another.
334 root should use os.sep to separate directories
334 root should use os.sep to separate directories
335 n1 should use os.sep to separate directories
335 n1 should use os.sep to separate directories
336 n2 should use "/" to separate directories
336 n2 should use "/" to separate directories
337 returns an os.sep-separated path.
337 returns an os.sep-separated path.
338
338
339 If n1 is a relative path, it's assumed it's
339 If n1 is a relative path, it's assumed it's
340 relative to root.
340 relative to root.
341 n2 should always be relative to root.
341 n2 should always be relative to root.
342 '''
342 '''
343 if not n1: return localpath(n2)
343 if not n1: return localpath(n2)
344 if os.path.isabs(n1):
344 if os.path.isabs(n1):
345 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
345 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
346 return os.path.join(root, localpath(n2))
346 return os.path.join(root, localpath(n2))
347 n2 = '/'.join((pconvert(root), n2))
347 n2 = '/'.join((pconvert(root), n2))
348 a, b = n1.split(os.sep), n2.split('/')
348 a, b = n1.split(os.sep), n2.split('/')
349 a.reverse()
349 a.reverse()
350 b.reverse()
350 b.reverse()
351 while a and b and a[-1] == b[-1]:
351 while a and b and a[-1] == b[-1]:
352 a.pop()
352 a.pop()
353 b.pop()
353 b.pop()
354 b.reverse()
354 b.reverse()
355 return os.sep.join((['..'] * len(a)) + b)
355 return os.sep.join((['..'] * len(a)) + b)
356
356
357 def canonpath(root, cwd, myname):
357 def canonpath(root, cwd, myname):
358 """return the canonical path of myname, given cwd and root"""
358 """return the canonical path of myname, given cwd and root"""
359 if root == os.sep:
359 if root == os.sep:
360 rootsep = os.sep
360 rootsep = os.sep
361 elif root.endswith(os.sep):
361 elif root.endswith(os.sep):
362 rootsep = root
362 rootsep = root
363 else:
363 else:
364 rootsep = root + os.sep
364 rootsep = root + os.sep
365 name = myname
365 name = myname
366 if not os.path.isabs(name):
366 if not os.path.isabs(name):
367 name = os.path.join(root, cwd, name)
367 name = os.path.join(root, cwd, name)
368 name = os.path.normpath(name)
368 name = os.path.normpath(name)
369 if name != rootsep and name.startswith(rootsep):
369 if name != rootsep and name.startswith(rootsep):
370 name = name[len(rootsep):]
370 name = name[len(rootsep):]
371 audit_path(name)
371 audit_path(name)
372 return pconvert(name)
372 return pconvert(name)
373 elif name == root:
373 elif name == root:
374 return ''
374 return ''
375 else:
375 else:
376 # Determine whether `name' is in the hierarchy at or beneath `root',
376 # Determine whether `name' is in the hierarchy at or beneath `root',
377 # by iterating name=dirname(name) until that causes no change (can't
377 # by iterating name=dirname(name) until that causes no change (can't
378 # check name == '/', because that doesn't work on windows). For each
378 # check name == '/', because that doesn't work on windows). For each
379 # `name', compare dev/inode numbers. If they match, the list `rel'
379 # `name', compare dev/inode numbers. If they match, the list `rel'
380 # holds the reversed list of components making up the relative file
380 # holds the reversed list of components making up the relative file
381 # name we want.
381 # name we want.
382 root_st = os.stat(root)
382 root_st = os.stat(root)
383 rel = []
383 rel = []
384 while True:
384 while True:
385 try:
385 try:
386 name_st = os.stat(name)
386 name_st = os.stat(name)
387 except OSError:
387 except OSError:
388 break
388 break
389 if samestat(name_st, root_st):
389 if samestat(name_st, root_st):
390 if not rel:
390 if not rel:
391 # name was actually the same as root (maybe a symlink)
391 # name was actually the same as root (maybe a symlink)
392 return ''
392 return ''
393 rel.reverse()
393 rel.reverse()
394 name = os.path.join(*rel)
394 name = os.path.join(*rel)
395 audit_path(name)
395 audit_path(name)
396 return pconvert(name)
396 return pconvert(name)
397 dirname, basename = os.path.split(name)
397 dirname, basename = os.path.split(name)
398 rel.append(basename)
398 rel.append(basename)
399 if dirname == name:
399 if dirname == name:
400 break
400 break
401 name = dirname
401 name = dirname
402
402
403 raise Abort('%s not under root' % myname)
403 raise Abort('%s not under root' % myname)
404
404
405 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None):
405 def matcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None):
406 return _matcher(canonroot, cwd, names, inc, exc, 'glob', src)
406 return _matcher(canonroot, cwd, names, inc, exc, 'glob', src)
407
407
408 def cmdmatcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None,
408 def cmdmatcher(canonroot, cwd='', names=[], inc=[], exc=[], src=None,
409 globbed=False, default=None):
409 globbed=False, default=None):
410 default = default or 'relpath'
410 default = default or 'relpath'
411 if default == 'relpath' and not globbed:
411 if default == 'relpath' and not globbed:
412 names = expand_glob(names)
412 names = expand_glob(names)
413 return _matcher(canonroot, cwd, names, inc, exc, default, src)
413 return _matcher(canonroot, cwd, names, inc, exc, default, src)
414
414
415 def _matcher(canonroot, cwd, names, inc, exc, dflt_pat, src):
415 def _matcher(canonroot, cwd, names, inc, exc, dflt_pat, src):
416 """build a function to match a set of file patterns
416 """build a function to match a set of file patterns
417
417
418 arguments:
418 arguments:
419 canonroot - the canonical root of the tree you're matching against
419 canonroot - the canonical root of the tree you're matching against
420 cwd - the current working directory, if relevant
420 cwd - the current working directory, if relevant
421 names - patterns to find
421 names - patterns to find
422 inc - patterns to include
422 inc - patterns to include
423 exc - patterns to exclude
423 exc - patterns to exclude
424 dflt_pat - if a pattern in names has no explicit type, assume this one
424 dflt_pat - if a pattern in names has no explicit type, assume this one
425 src - where these patterns came from (e.g. .hgignore)
425 src - where these patterns came from (e.g. .hgignore)
426
426
427 a pattern is one of:
427 a pattern is one of:
428 'glob:<glob>' - a glob relative to cwd
428 'glob:<glob>' - a glob relative to cwd
429 're:<regexp>' - a regular expression
429 're:<regexp>' - a regular expression
430 'path:<path>' - a path relative to canonroot
430 'path:<path>' - a path relative to canonroot
431 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
431 'relglob:<glob>' - an unrooted glob (*.c matches C files in all dirs)
432 'relpath:<path>' - a path relative to cwd
432 'relpath:<path>' - a path relative to cwd
433 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
433 'relre:<regexp>' - a regexp that doesn't have to match the start of a name
434 '<something>' - one of the cases above, selected by the dflt_pat argument
434 '<something>' - one of the cases above, selected by the dflt_pat argument
435
435
436 returns:
436 returns:
437 a 3-tuple containing
437 a 3-tuple containing
438 - list of roots (places where one should start a recursive walk of the fs);
438 - list of roots (places where one should start a recursive walk of the fs);
439 this often matches the explicit non-pattern names passed in, but also
439 this often matches the explicit non-pattern names passed in, but also
440 includes the initial part of glob: patterns that has no glob characters
440 includes the initial part of glob: patterns that has no glob characters
441 - a bool match(filename) function
441 - a bool match(filename) function
442 - a bool indicating if any patterns were passed in
442 - a bool indicating if any patterns were passed in
443 """
443 """
444
444
445 # a common case: no patterns at all
445 # a common case: no patterns at all
446 if not names and not inc and not exc:
446 if not names and not inc and not exc:
447 return [], always, False
447 return [], always, False
448
448
449 def contains_glob(name):
449 def contains_glob(name):
450 for c in name:
450 for c in name:
451 if c in _globchars: return True
451 if c in _globchars: return True
452 return False
452 return False
453
453
454 def regex(kind, name, tail):
454 def regex(kind, name, tail):
455 '''convert a pattern into a regular expression'''
455 '''convert a pattern into a regular expression'''
456 if not name:
456 if not name:
457 return ''
457 return ''
458 if kind == 're':
458 if kind == 're':
459 return name
459 return name
460 elif kind == 'path':
460 elif kind == 'path':
461 return '^' + re.escape(name) + '(?:/|$)'
461 return '^' + re.escape(name) + '(?:/|$)'
462 elif kind == 'relglob':
462 elif kind == 'relglob':
463 return globre(name, '(?:|.*/)', tail)
463 return globre(name, '(?:|.*/)', tail)
464 elif kind == 'relpath':
464 elif kind == 'relpath':
465 return re.escape(name) + '(?:/|$)'
465 return re.escape(name) + '(?:/|$)'
466 elif kind == 'relre':
466 elif kind == 'relre':
467 if name.startswith('^'):
467 if name.startswith('^'):
468 return name
468 return name
469 return '.*' + name
469 return '.*' + name
470 return globre(name, '', tail)
470 return globre(name, '', tail)
471
471
472 def matchfn(pats, tail):
472 def matchfn(pats, tail):
473 """build a matching function from a set of patterns"""
473 """build a matching function from a set of patterns"""
474 if not pats:
474 if not pats:
475 return
475 return
476 try:
476 try:
477 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
477 pat = '(?:%s)' % '|'.join([regex(k, p, tail) for (k, p) in pats])
478 return re.compile(pat).match
478 return re.compile(pat).match
479 except re.error:
479 except re.error:
480 for k, p in pats:
480 for k, p in pats:
481 try:
481 try:
482 re.compile('(?:%s)' % regex(k, p, tail))
482 re.compile('(?:%s)' % regex(k, p, tail))
483 except re.error:
483 except re.error:
484 if src:
484 if src:
485 raise Abort("%s: invalid pattern (%s): %s" %
485 raise Abort("%s: invalid pattern (%s): %s" %
486 (src, k, p))
486 (src, k, p))
487 else:
487 else:
488 raise Abort("invalid pattern (%s): %s" % (k, p))
488 raise Abort("invalid pattern (%s): %s" % (k, p))
489 raise Abort("invalid pattern")
489 raise Abort("invalid pattern")
490
490
491 def globprefix(pat):
491 def globprefix(pat):
492 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
492 '''return the non-glob prefix of a path, e.g. foo/* -> foo'''
493 root = []
493 root = []
494 for p in pat.split('/'):
494 for p in pat.split('/'):
495 if contains_glob(p): break
495 if contains_glob(p): break
496 root.append(p)
496 root.append(p)
497 return '/'.join(root) or '.'
497 return '/'.join(root) or '.'
498
498
499 def normalizepats(names, default):
499 def normalizepats(names, default):
500 pats = []
500 pats = []
501 roots = []
501 roots = []
502 anypats = False
502 anypats = False
503 for kind, name in [patkind(p, default) for p in names]:
503 for kind, name in [patkind(p, default) for p in names]:
504 if kind in ('glob', 'relpath'):
504 if kind in ('glob', 'relpath'):
505 name = canonpath(canonroot, cwd, name)
505 name = canonpath(canonroot, cwd, name)
506 elif kind in ('relglob', 'path'):
506 elif kind in ('relglob', 'path'):
507 name = normpath(name)
507 name = normpath(name)
508
508
509 pats.append((kind, name))
509 pats.append((kind, name))
510
510
511 if kind in ('glob', 're', 'relglob', 'relre'):
511 if kind in ('glob', 're', 'relglob', 'relre'):
512 anypats = True
512 anypats = True
513
513
514 if kind == 'glob':
514 if kind == 'glob':
515 root = globprefix(name)
515 root = globprefix(name)
516 roots.append(root)
516 roots.append(root)
517 elif kind in ('relpath', 'path'):
517 elif kind in ('relpath', 'path'):
518 roots.append(name or '.')
518 roots.append(name or '.')
519 elif kind == 'relglob':
519 elif kind == 'relglob':
520 roots.append('.')
520 roots.append('.')
521 return roots, pats, anypats
521 return roots, pats, anypats
522
522
523 roots, pats, anypats = normalizepats(names, dflt_pat)
523 roots, pats, anypats = normalizepats(names, dflt_pat)
524
524
525 patmatch = matchfn(pats, '$') or always
525 patmatch = matchfn(pats, '$') or always
526 incmatch = always
526 incmatch = always
527 if inc:
527 if inc:
528 dummy, inckinds, dummy = normalizepats(inc, 'glob')
528 dummy, inckinds, dummy = normalizepats(inc, 'glob')
529 incmatch = matchfn(inckinds, '(?:/|$)')
529 incmatch = matchfn(inckinds, '(?:/|$)')
530 excmatch = lambda fn: False
530 excmatch = lambda fn: False
531 if exc:
531 if exc:
532 dummy, exckinds, dummy = normalizepats(exc, 'glob')
532 dummy, exckinds, dummy = normalizepats(exc, 'glob')
533 excmatch = matchfn(exckinds, '(?:/|$)')
533 excmatch = matchfn(exckinds, '(?:/|$)')
534
534
535 if not names and inc and not exc:
535 if not names and inc and not exc:
536 # common case: hgignore patterns
536 # common case: hgignore patterns
537 match = incmatch
537 match = incmatch
538 else:
538 else:
539 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
539 match = lambda fn: incmatch(fn) and not excmatch(fn) and patmatch(fn)
540
540
541 return (roots, match, (inc or exc or anypats) and True)
541 return (roots, match, (inc or exc or anypats) and True)
542
542
543 _hgexecutable = None
543 _hgexecutable = None
544
544
545 def set_hgexecutable(path):
545 def set_hgexecutable(path):
546 """remember location of the 'hg' executable if easily possible
546 """remember location of the 'hg' executable if easily possible
547
547
548 path might be None or empty if hg was loaded as a module,
548 path might be None or empty if hg was loaded as a module,
549 fall back to 'hg' in this case.
549 fall back to 'hg' in this case.
550 """
550 """
551 global _hgexecutable
551 global _hgexecutable
552 _hgexecutable = path and os.path.abspath(path) or 'hg'
552 _hgexecutable = path and os.path.abspath(path) or 'hg'
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 if 'HG' not in os.environ:
579 if 'HG' not in os.environ:
580 os.environ['HG'] = _hgexecutable
580 os.environ['HG'] = _hgexecutable
581 if cwd is not None and oldcwd != cwd:
581 if cwd is not None and oldcwd != cwd:
582 os.chdir(cwd)
582 os.chdir(cwd)
583 rc = os.system(cmd)
583 rc = os.system(cmd)
584 if sys.platform == 'OpenVMS' and rc & 1:
584 if sys.platform == 'OpenVMS' and rc & 1:
585 rc = 0
585 rc = 0
586 if rc and onerr:
586 if rc and onerr:
587 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
587 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
588 explain_exit(rc)[0])
588 explain_exit(rc)[0])
589 if errprefix:
589 if errprefix:
590 errmsg = '%s: %s' % (errprefix, errmsg)
590 errmsg = '%s: %s' % (errprefix, errmsg)
591 try:
591 try:
592 onerr.warn(errmsg + '\n')
592 onerr.warn(errmsg + '\n')
593 except AttributeError:
593 except AttributeError:
594 raise onerr(errmsg)
594 raise onerr(errmsg)
595 return rc
595 return rc
596 finally:
596 finally:
597 for k, v in oldenv.iteritems():
597 for k, v in oldenv.iteritems():
598 if v is None:
598 if v is None:
599 del os.environ[k]
599 del os.environ[k]
600 else:
600 else:
601 os.environ[k] = v
601 os.environ[k] = v
602 if cwd is not None and oldcwd != cwd:
602 if cwd is not None and oldcwd != cwd:
603 os.chdir(oldcwd)
603 os.chdir(oldcwd)
604
604
605 # os.path.lexists is not available on python2.3
605 # os.path.lexists is not available on python2.3
606 def lexists(filename):
606 def lexists(filename):
607 "test whether a file with this name exists. does not follow symlinks"
607 "test whether a file with this name exists. does not follow symlinks"
608 try:
608 try:
609 os.lstat(filename)
609 os.lstat(filename)
610 except:
610 except:
611 return False
611 return False
612 return True
612 return True
613
613
614 def rename(src, dst):
614 def rename(src, dst):
615 """forcibly rename a file"""
615 """forcibly rename a file"""
616 try:
616 try:
617 os.rename(src, dst)
617 os.rename(src, dst)
618 except OSError, err:
618 except OSError, err:
619 # on windows, rename to existing file is not allowed, so we
619 # on windows, rename to existing file is not allowed, so we
620 # must delete destination first. but if file is open, unlink
620 # must delete destination first. but if file is open, unlink
621 # schedules it for delete but does not delete it. rename
621 # schedules it for delete but does not delete it. rename
622 # happens immediately even for open files, so we create
622 # happens immediately even for open files, so we create
623 # temporary file, delete it, rename destination to that name,
623 # temporary file, delete it, rename destination to that name,
624 # then delete that. then rename is safe to do.
624 # then delete that. then rename is safe to do.
625 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
625 fd, temp = tempfile.mkstemp(dir=os.path.dirname(dst) or '.')
626 os.close(fd)
626 os.close(fd)
627 os.unlink(temp)
627 os.unlink(temp)
628 os.rename(dst, temp)
628 os.rename(dst, temp)
629 os.unlink(temp)
629 os.unlink(temp)
630 os.rename(src, dst)
630 os.rename(src, dst)
631
631
632 def unlink(f):
632 def unlink(f):
633 """unlink and remove the directory if it is empty"""
633 """unlink and remove the directory if it is empty"""
634 os.unlink(f)
634 os.unlink(f)
635 # try removing directories that might now be empty
635 # try removing directories that might now be empty
636 try:
636 try:
637 os.removedirs(os.path.dirname(f))
637 os.removedirs(os.path.dirname(f))
638 except OSError:
638 except OSError:
639 pass
639 pass
640
640
641 def copyfile(src, dest):
641 def copyfile(src, dest):
642 "copy a file, preserving mode"
642 "copy a file, preserving mode"
643 if os.path.islink(src):
643 if os.path.islink(src):
644 try:
644 try:
645 os.unlink(dest)
645 os.unlink(dest)
646 except:
646 except:
647 pass
647 pass
648 os.symlink(os.readlink(src), dest)
648 os.symlink(os.readlink(src), dest)
649 else:
649 else:
650 try:
650 try:
651 shutil.copyfile(src, dest)
651 shutil.copyfile(src, dest)
652 shutil.copymode(src, dest)
652 shutil.copymode(src, dest)
653 except shutil.Error, inst:
653 except shutil.Error, inst:
654 raise Abort(str(inst))
654 raise Abort(str(inst))
655
655
656 def copyfiles(src, dst, hardlink=None):
656 def copyfiles(src, dst, hardlink=None):
657 """Copy a directory tree using hardlinks if possible"""
657 """Copy a directory tree using hardlinks if possible"""
658
658
659 if hardlink is None:
659 if hardlink is None:
660 hardlink = (os.stat(src).st_dev ==
660 hardlink = (os.stat(src).st_dev ==
661 os.stat(os.path.dirname(dst)).st_dev)
661 os.stat(os.path.dirname(dst)).st_dev)
662
662
663 if os.path.isdir(src):
663 if os.path.isdir(src):
664 os.mkdir(dst)
664 os.mkdir(dst)
665 for name in os.listdir(src):
665 for name in os.listdir(src):
666 srcname = os.path.join(src, name)
666 srcname = os.path.join(src, name)
667 dstname = os.path.join(dst, name)
667 dstname = os.path.join(dst, name)
668 copyfiles(srcname, dstname, hardlink)
668 copyfiles(srcname, dstname, hardlink)
669 else:
669 else:
670 if hardlink:
670 if hardlink:
671 try:
671 try:
672 os_link(src, dst)
672 os_link(src, dst)
673 except (IOError, OSError):
673 except (IOError, OSError):
674 hardlink = False
674 hardlink = False
675 shutil.copy(src, dst)
675 shutil.copy(src, dst)
676 else:
676 else:
677 shutil.copy(src, dst)
677 shutil.copy(src, dst)
678
678
679 def audit_path(path):
679 def audit_path(path):
680 """Abort if path contains dangerous components"""
680 """Abort if path contains dangerous components"""
681 parts = os.path.normcase(path).split(os.sep)
681 parts = os.path.normcase(path).split(os.sep)
682 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
682 if (os.path.splitdrive(path)[0] or parts[0] in ('.hg', '')
683 or os.pardir in parts):
683 or os.pardir in parts):
684 raise Abort(_("path contains illegal component: %s") % path)
684 raise Abort(_("path contains illegal component: %s") % path)
685
685
686 def _makelock_file(info, pathname):
686 def _makelock_file(info, pathname):
687 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
687 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
688 os.write(ld, info)
688 os.write(ld, info)
689 os.close(ld)
689 os.close(ld)
690
690
691 def _readlock_file(pathname):
691 def _readlock_file(pathname):
692 return posixfile(pathname).read()
692 return posixfile(pathname).read()
693
693
694 def nlinks(pathname):
694 def nlinks(pathname):
695 """Return number of hardlinks for the given file."""
695 """Return number of hardlinks for the given file."""
696 return os.lstat(pathname).st_nlink
696 return os.lstat(pathname).st_nlink
697
697
698 if hasattr(os, 'link'):
698 if hasattr(os, 'link'):
699 os_link = os.link
699 os_link = os.link
700 else:
700 else:
701 def os_link(src, dst):
701 def os_link(src, dst):
702 raise OSError(0, _("Hardlinks not supported"))
702 raise OSError(0, _("Hardlinks not supported"))
703
703
704 def fstat(fp):
704 def fstat(fp):
705 '''stat file object that may not have fileno method.'''
705 '''stat file object that may not have fileno method.'''
706 try:
706 try:
707 return os.fstat(fp.fileno())
707 return os.fstat(fp.fileno())
708 except AttributeError:
708 except AttributeError:
709 return os.stat(fp.name)
709 return os.stat(fp.name)
710
710
711 posixfile = file
711 posixfile = file
712
712
713 def is_win_9x():
713 def is_win_9x():
714 '''return true if run on windows 95, 98 or me.'''
714 '''return true if run on windows 95, 98 or me.'''
715 try:
715 try:
716 return sys.getwindowsversion()[3] == 1
716 return sys.getwindowsversion()[3] == 1
717 except AttributeError:
717 except AttributeError:
718 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
718 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
719
719
720 getuser_fallback = None
720 getuser_fallback = None
721
721
722 def getuser():
722 def getuser():
723 '''return name of current user'''
723 '''return name of current user'''
724 try:
724 try:
725 return getpass.getuser()
725 return getpass.getuser()
726 except ImportError:
726 except ImportError:
727 # import of pwd will fail on windows - try fallback
727 # import of pwd will fail on windows - try fallback
728 if getuser_fallback:
728 if getuser_fallback:
729 return getuser_fallback()
729 return getuser_fallback()
730 # raised if win32api not available
730 # raised if win32api not available
731 raise Abort(_('user name not available - set USERNAME '
731 raise Abort(_('user name not available - set USERNAME '
732 'environment variable'))
732 'environment variable'))
733
733
734 def username(uid=None):
734 def username(uid=None):
735 """Return the name of the user with the given uid.
735 """Return the name of the user with the given uid.
736
736
737 If uid is None, return the name of the current user."""
737 If uid is None, return the name of the current user."""
738 try:
738 try:
739 import pwd
739 import pwd
740 if uid is None:
740 if uid is None:
741 uid = os.getuid()
741 uid = os.getuid()
742 try:
742 try:
743 return pwd.getpwuid(uid)[0]
743 return pwd.getpwuid(uid)[0]
744 except KeyError:
744 except KeyError:
745 return str(uid)
745 return str(uid)
746 except ImportError:
746 except ImportError:
747 return None
747 return None
748
748
749 def groupname(gid=None):
749 def groupname(gid=None):
750 """Return the name of the group with the given gid.
750 """Return the name of the group with the given gid.
751
751
752 If gid is None, return the name of the current group."""
752 If gid is None, return the name of the current group."""
753 try:
753 try:
754 import grp
754 import grp
755 if gid is None:
755 if gid is None:
756 gid = os.getgid()
756 gid = os.getgid()
757 try:
757 try:
758 return grp.getgrgid(gid)[0]
758 return grp.getgrgid(gid)[0]
759 except KeyError:
759 except KeyError:
760 return str(gid)
760 return str(gid)
761 except ImportError:
761 except ImportError:
762 return None
762 return None
763
763
764 # File system features
764 # File system features
765
765
766 def checkfolding(path):
766 def checkfolding(path):
767 """
767 """
768 Check whether the given path is on a case-sensitive filesystem
768 Check whether the given path is on a case-sensitive filesystem
769
769
770 Requires a path (like /foo/.hg) ending with a foldable final
770 Requires a path (like /foo/.hg) ending with a foldable final
771 directory component.
771 directory component.
772 """
772 """
773 s1 = os.stat(path)
773 s1 = os.stat(path)
774 d, b = os.path.split(path)
774 d, b = os.path.split(path)
775 p2 = os.path.join(d, b.upper())
775 p2 = os.path.join(d, b.upper())
776 if path == p2:
776 if path == p2:
777 p2 = os.path.join(d, b.lower())
777 p2 = os.path.join(d, b.lower())
778 try:
778 try:
779 s2 = os.stat(p2)
779 s2 = os.stat(p2)
780 if s2 == s1:
780 if s2 == s1:
781 return False
781 return False
782 return True
782 return True
783 except:
783 except:
784 return True
784 return True
785
785
786 def checkexec(path):
786 def checkexec(path):
787 """
787 """
788 Check whether the given path is on a filesystem with UNIX-like exec flags
788 Check whether the given path is on a filesystem with UNIX-like exec flags
789
789
790 Requires a directory (like /foo/.hg)
790 Requires a directory (like /foo/.hg)
791 """
791 """
792 fh, fn = tempfile.mkstemp("", "", path)
792 fh, fn = tempfile.mkstemp("", "", path)
793 os.close(fh)
793 os.close(fh)
794 m = os.stat(fn).st_mode
794 m = os.stat(fn).st_mode
795 os.chmod(fn, m ^ 0111)
795 os.chmod(fn, m ^ 0111)
796 r = (os.stat(fn).st_mode != m)
796 r = (os.stat(fn).st_mode != m)
797 os.unlink(fn)
797 os.unlink(fn)
798 return r
798 return r
799
799
800 def execfunc(path, fallback):
800 def execfunc(path, fallback):
801 '''return an is_exec() function with default to fallback'''
801 '''return an is_exec() function with default to fallback'''
802 if checkexec(path):
802 if checkexec(path):
803 return lambda x: is_exec(os.path.join(path, x))
803 return lambda x: is_exec(os.path.join(path, x))
804 return fallback
804 return fallback
805
805
806 def checklink(path):
806 def checklink(path):
807 """check whether the given path is on a symlink-capable filesystem"""
807 """check whether the given path is on a symlink-capable filesystem"""
808 # mktemp is not racy because symlink creation will fail if the
808 # mktemp is not racy because symlink creation will fail if the
809 # file already exists
809 # file already exists
810 name = tempfile.mktemp(dir=path)
810 name = tempfile.mktemp(dir=path)
811 try:
811 try:
812 os.symlink(".", name)
812 os.symlink(".", name)
813 os.unlink(name)
813 os.unlink(name)
814 return True
814 return True
815 except (OSError, AttributeError):
815 except (OSError, AttributeError):
816 return False
816 return False
817
817
818 def linkfunc(path, fallback):
818 def linkfunc(path, fallback):
819 '''return an is_link() function with default to fallback'''
819 '''return an is_link() function with default to fallback'''
820 if checklink(path):
820 if checklink(path):
821 return lambda x: os.path.islink(os.path.join(path, x))
821 return lambda x: os.path.islink(os.path.join(path, x))
822 return fallback
822 return fallback
823
823
824 _umask = os.umask(0)
824 _umask = os.umask(0)
825 os.umask(_umask)
825 os.umask(_umask)
826
826
827 def needbinarypatch():
827 def needbinarypatch():
828 """return True if patches should be applied in binary mode by default."""
828 """return True if patches should be applied in binary mode by default."""
829 return os.name == 'nt'
829 return os.name == 'nt'
830
830
831 # Platform specific variants
831 # Platform specific variants
832 if os.name == 'nt':
832 if os.name == 'nt':
833 import msvcrt
833 import msvcrt
834 nulldev = 'NUL:'
834 nulldev = 'NUL:'
835
835
836 class winstdout:
836 class winstdout:
837 '''stdout on windows misbehaves if sent through a pipe'''
837 '''stdout on windows misbehaves if sent through a pipe'''
838
838
839 def __init__(self, fp):
839 def __init__(self, fp):
840 self.fp = fp
840 self.fp = fp
841
841
842 def __getattr__(self, key):
842 def __getattr__(self, key):
843 return getattr(self.fp, key)
843 return getattr(self.fp, key)
844
844
845 def close(self):
845 def close(self):
846 try:
846 try:
847 self.fp.close()
847 self.fp.close()
848 except: pass
848 except: pass
849
849
850 def write(self, s):
850 def write(self, s):
851 try:
851 try:
852 return self.fp.write(s)
852 return self.fp.write(s)
853 except IOError, inst:
853 except IOError, inst:
854 if inst.errno != 0: raise
854 if inst.errno != 0: raise
855 self.close()
855 self.close()
856 raise IOError(errno.EPIPE, 'Broken pipe')
856 raise IOError(errno.EPIPE, 'Broken pipe')
857
857
858 def flush(self):
858 def flush(self):
859 try:
859 try:
860 return self.fp.flush()
860 return self.fp.flush()
861 except IOError, inst:
861 except IOError, inst:
862 if inst.errno != errno.EINVAL: raise
862 if inst.errno != errno.EINVAL: raise
863 self.close()
863 self.close()
864 raise IOError(errno.EPIPE, 'Broken pipe')
864 raise IOError(errno.EPIPE, 'Broken pipe')
865
865
866 sys.stdout = winstdout(sys.stdout)
866 sys.stdout = winstdout(sys.stdout)
867
867
868 def system_rcpath():
868 def system_rcpath():
869 try:
869 try:
870 return system_rcpath_win32()
870 return system_rcpath_win32()
871 except:
871 except:
872 return [r'c:\mercurial\mercurial.ini']
872 return [r'c:\mercurial\mercurial.ini']
873
873
874 def user_rcpath():
874 def user_rcpath():
875 '''return os-specific hgrc search path to the user dir'''
875 '''return os-specific hgrc search path to the user dir'''
876 try:
876 try:
877 userrc = user_rcpath_win32()
877 userrc = user_rcpath_win32()
878 except:
878 except:
879 userrc = os.path.join(os.path.expanduser('~'), 'mercurial.ini')
879 userrc = os.path.join(os.path.expanduser('~'), 'mercurial.ini')
880 path = [userrc]
880 path = [userrc]
881 userprofile = os.environ.get('USERPROFILE')
881 userprofile = os.environ.get('USERPROFILE')
882 if userprofile:
882 if userprofile:
883 path.append(os.path.join(userprofile, 'mercurial.ini'))
883 path.append(os.path.join(userprofile, 'mercurial.ini'))
884 return path
884 return path
885
885
886 def parse_patch_output(output_line):
886 def parse_patch_output(output_line):
887 """parses the output produced by patch and returns the file name"""
887 """parses the output produced by patch and returns the file name"""
888 pf = output_line[14:]
888 pf = output_line[14:]
889 if pf[0] == '`':
889 if pf[0] == '`':
890 pf = pf[1:-1] # Remove the quotes
890 pf = pf[1:-1] # Remove the quotes
891 return pf
891 return pf
892
892
893 def testpid(pid):
893 def testpid(pid):
894 '''return False if pid dead, True if running or not known'''
894 '''return False if pid dead, True if running or not known'''
895 return True
895 return True
896
896
897 def set_exec(f, mode):
897 def set_exec(f, mode):
898 pass
898 pass
899
899
900 def set_link(f, mode):
900 def set_link(f, mode):
901 pass
901 pass
902
902
903 def set_binary(fd):
903 def set_binary(fd):
904 msvcrt.setmode(fd.fileno(), os.O_BINARY)
904 msvcrt.setmode(fd.fileno(), os.O_BINARY)
905
905
906 def pconvert(path):
906 def pconvert(path):
907 return path.replace("\\", "/")
907 return path.replace("\\", "/")
908
908
909 def localpath(path):
909 def localpath(path):
910 return path.replace('/', '\\')
910 return path.replace('/', '\\')
911
911
912 def normpath(path):
912 def normpath(path):
913 return pconvert(os.path.normpath(path))
913 return pconvert(os.path.normpath(path))
914
914
915 makelock = _makelock_file
915 makelock = _makelock_file
916 readlock = _readlock_file
916 readlock = _readlock_file
917
917
918 def samestat(s1, s2):
918 def samestat(s1, s2):
919 return False
919 return False
920
920
921 # A sequence of backslashes is special iff it precedes a double quote:
921 # A sequence of backslashes is special iff it precedes a double quote:
922 # - if there's an even number of backslashes, the double quote is not
922 # - if there's an even number of backslashes, the double quote is not
923 # quoted (i.e. it ends the quoted region)
923 # quoted (i.e. it ends the quoted region)
924 # - if there's an odd number of backslashes, the double quote is quoted
924 # - if there's an odd number of backslashes, the double quote is quoted
925 # - in both cases, every pair of backslashes is unquoted into a single
925 # - in both cases, every pair of backslashes is unquoted into a single
926 # backslash
926 # backslash
927 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
927 # (See http://msdn2.microsoft.com/en-us/library/a1y7w461.aspx )
928 # So, to quote a string, we must surround it in double quotes, double
928 # So, to quote a string, we must surround it in double quotes, double
929 # the number of backslashes that preceed double quotes and add another
929 # the number of backslashes that preceed double quotes and add another
930 # backslash before every double quote (being careful with the double
930 # backslash before every double quote (being careful with the double
931 # quote we've appended to the end)
931 # quote we've appended to the end)
932 _quotere = None
932 _quotere = None
933 def shellquote(s):
933 def shellquote(s):
934 global _quotere
934 global _quotere
935 if _quotere is None:
935 if _quotere is None:
936 _quotere = re.compile(r'(\\*)("|\\$)')
936 _quotere = re.compile(r'(\\*)("|\\$)')
937 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
937 return '"%s"' % _quotere.sub(r'\1\1\\\2', s)
938
938
939 def explain_exit(code):
939 def explain_exit(code):
940 return _("exited with status %d") % code, code
940 return _("exited with status %d") % code, code
941
941
942 # if you change this stub into a real check, please try to implement the
942 # if you change this stub into a real check, please try to implement the
943 # username and groupname functions above, too.
943 # username and groupname functions above, too.
944 def isowner(fp, st=None):
944 def isowner(fp, st=None):
945 return True
945 return True
946
946
947 def find_in_path(name, path, default=None):
947 def find_in_path(name, path, default=None):
948 '''find name in search path. path can be string (will be split
948 '''find name in search path. path can be string (will be split
949 with os.pathsep), or iterable thing that returns strings. if name
949 with os.pathsep), or iterable thing that returns strings. if name
950 found, return path to name. else return default. name is looked up
950 found, return path to name. else return default. name is looked up
951 using cmd.exe rules, using PATHEXT.'''
951 using cmd.exe rules, using PATHEXT.'''
952 if isinstance(path, str):
952 if isinstance(path, str):
953 path = path.split(os.pathsep)
953 path = path.split(os.pathsep)
954
954
955 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
955 pathext = os.environ.get('PATHEXT', '.COM;.EXE;.BAT;.CMD')
956 pathext = pathext.lower().split(os.pathsep)
956 pathext = pathext.lower().split(os.pathsep)
957 isexec = os.path.splitext(name)[1].lower() in pathext
957 isexec = os.path.splitext(name)[1].lower() in pathext
958
958
959 for p in path:
959 for p in path:
960 p_name = os.path.join(p, name)
960 p_name = os.path.join(p, name)
961
961
962 if isexec and os.path.exists(p_name):
962 if isexec and os.path.exists(p_name):
963 return p_name
963 return p_name
964
964
965 for ext in pathext:
965 for ext in pathext:
966 p_name_ext = p_name + ext
966 p_name_ext = p_name + ext
967 if os.path.exists(p_name_ext):
967 if os.path.exists(p_name_ext):
968 return p_name_ext
968 return p_name_ext
969 return default
969 return default
970
970
971 def set_signal_handler():
972 try:
973 set_signal_handler_win32()
974 except NameError:
975 pass
976
971 try:
977 try:
972 # override functions with win32 versions if possible
978 # override functions with win32 versions if possible
973 from util_win32 import *
979 from util_win32 import *
974 if not is_win_9x():
980 if not is_win_9x():
975 posixfile = posixfile_nt
981 posixfile = posixfile_nt
976 except ImportError:
982 except ImportError:
977 pass
983 pass
978
984
979 else:
985 else:
980 nulldev = '/dev/null'
986 nulldev = '/dev/null'
981
987
982 def rcfiles(path):
988 def rcfiles(path):
983 rcs = [os.path.join(path, 'hgrc')]
989 rcs = [os.path.join(path, 'hgrc')]
984 rcdir = os.path.join(path, 'hgrc.d')
990 rcdir = os.path.join(path, 'hgrc.d')
985 try:
991 try:
986 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
992 rcs.extend([os.path.join(rcdir, f) for f in os.listdir(rcdir)
987 if f.endswith(".rc")])
993 if f.endswith(".rc")])
988 except OSError:
994 except OSError:
989 pass
995 pass
990 return rcs
996 return rcs
991
997
992 def system_rcpath():
998 def system_rcpath():
993 path = []
999 path = []
994 # old mod_python does not set sys.argv
1000 # old mod_python does not set sys.argv
995 if len(getattr(sys, 'argv', [])) > 0:
1001 if len(getattr(sys, 'argv', [])) > 0:
996 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
1002 path.extend(rcfiles(os.path.dirname(sys.argv[0]) +
997 '/../etc/mercurial'))
1003 '/../etc/mercurial'))
998 path.extend(rcfiles('/etc/mercurial'))
1004 path.extend(rcfiles('/etc/mercurial'))
999 return path
1005 return path
1000
1006
1001 def user_rcpath():
1007 def user_rcpath():
1002 return [os.path.expanduser('~/.hgrc')]
1008 return [os.path.expanduser('~/.hgrc')]
1003
1009
1004 def parse_patch_output(output_line):
1010 def parse_patch_output(output_line):
1005 """parses the output produced by patch and returns the file name"""
1011 """parses the output produced by patch and returns the file name"""
1006 pf = output_line[14:]
1012 pf = output_line[14:]
1007 if os.sys.platform == 'OpenVMS':
1013 if os.sys.platform == 'OpenVMS':
1008 if pf[0] == '`':
1014 if pf[0] == '`':
1009 pf = pf[1:-1] # Remove the quotes
1015 pf = pf[1:-1] # Remove the quotes
1010 else:
1016 else:
1011 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1017 if pf.startswith("'") and pf.endswith("'") and " " in pf:
1012 pf = pf[1:-1] # Remove the quotes
1018 pf = pf[1:-1] # Remove the quotes
1013 return pf
1019 return pf
1014
1020
1015 def is_exec(f):
1021 def is_exec(f):
1016 """check whether a file is executable"""
1022 """check whether a file is executable"""
1017 return (os.lstat(f).st_mode & 0100 != 0)
1023 return (os.lstat(f).st_mode & 0100 != 0)
1018
1024
1019 def set_exec(f, mode):
1025 def set_exec(f, mode):
1020 s = os.lstat(f).st_mode
1026 s = os.lstat(f).st_mode
1021 if (s & 0100 != 0) == mode:
1027 if (s & 0100 != 0) == mode:
1022 return
1028 return
1023 if mode:
1029 if mode:
1024 # Turn on +x for every +r bit when making a file executable
1030 # Turn on +x for every +r bit when making a file executable
1025 # and obey umask.
1031 # and obey umask.
1026 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1032 os.chmod(f, s | (s & 0444) >> 2 & ~_umask)
1027 else:
1033 else:
1028 os.chmod(f, s & 0666)
1034 os.chmod(f, s & 0666)
1029
1035
1030 def set_link(f, mode):
1036 def set_link(f, mode):
1031 """make a file a symbolic link/regular file
1037 """make a file a symbolic link/regular file
1032
1038
1033 if a file is changed to a link, its contents become the link data
1039 if a file is changed to a link, its contents become the link data
1034 if a link is changed to a file, its link data become its contents
1040 if a link is changed to a file, its link data become its contents
1035 """
1041 """
1036
1042
1037 m = os.path.islink(f)
1043 m = os.path.islink(f)
1038 if m == bool(mode):
1044 if m == bool(mode):
1039 return
1045 return
1040
1046
1041 if mode: # switch file to link
1047 if mode: # switch file to link
1042 data = file(f).read()
1048 data = file(f).read()
1043 os.unlink(f)
1049 os.unlink(f)
1044 os.symlink(data, f)
1050 os.symlink(data, f)
1045 else:
1051 else:
1046 data = os.readlink(f)
1052 data = os.readlink(f)
1047 os.unlink(f)
1053 os.unlink(f)
1048 file(f, "w").write(data)
1054 file(f, "w").write(data)
1049
1055
1050 def set_binary(fd):
1056 def set_binary(fd):
1051 pass
1057 pass
1052
1058
1053 def pconvert(path):
1059 def pconvert(path):
1054 return path
1060 return path
1055
1061
1056 def localpath(path):
1062 def localpath(path):
1057 return path
1063 return path
1058
1064
1059 normpath = os.path.normpath
1065 normpath = os.path.normpath
1060 samestat = os.path.samestat
1066 samestat = os.path.samestat
1061
1067
1062 def makelock(info, pathname):
1068 def makelock(info, pathname):
1063 try:
1069 try:
1064 os.symlink(info, pathname)
1070 os.symlink(info, pathname)
1065 except OSError, why:
1071 except OSError, why:
1066 if why.errno == errno.EEXIST:
1072 if why.errno == errno.EEXIST:
1067 raise
1073 raise
1068 else:
1074 else:
1069 _makelock_file(info, pathname)
1075 _makelock_file(info, pathname)
1070
1076
1071 def readlock(pathname):
1077 def readlock(pathname):
1072 try:
1078 try:
1073 return os.readlink(pathname)
1079 return os.readlink(pathname)
1074 except OSError, why:
1080 except OSError, why:
1075 if why.errno in (errno.EINVAL, errno.ENOSYS):
1081 if why.errno in (errno.EINVAL, errno.ENOSYS):
1076 return _readlock_file(pathname)
1082 return _readlock_file(pathname)
1077 else:
1083 else:
1078 raise
1084 raise
1079
1085
1080 def shellquote(s):
1086 def shellquote(s):
1081 if os.sys.platform == 'OpenVMS':
1087 if os.sys.platform == 'OpenVMS':
1082 return '"%s"' % s
1088 return '"%s"' % s
1083 else:
1089 else:
1084 return "'%s'" % s.replace("'", "'\\''")
1090 return "'%s'" % s.replace("'", "'\\''")
1085
1091
1086 def testpid(pid):
1092 def testpid(pid):
1087 '''return False if pid dead, True if running or not sure'''
1093 '''return False if pid dead, True if running or not sure'''
1088 if os.sys.platform == 'OpenVMS':
1094 if os.sys.platform == 'OpenVMS':
1089 return True
1095 return True
1090 try:
1096 try:
1091 os.kill(pid, 0)
1097 os.kill(pid, 0)
1092 return True
1098 return True
1093 except OSError, inst:
1099 except OSError, inst:
1094 return inst.errno != errno.ESRCH
1100 return inst.errno != errno.ESRCH
1095
1101
1096 def explain_exit(code):
1102 def explain_exit(code):
1097 """return a 2-tuple (desc, code) describing a process's status"""
1103 """return a 2-tuple (desc, code) describing a process's status"""
1098 if os.WIFEXITED(code):
1104 if os.WIFEXITED(code):
1099 val = os.WEXITSTATUS(code)
1105 val = os.WEXITSTATUS(code)
1100 return _("exited with status %d") % val, val
1106 return _("exited with status %d") % val, val
1101 elif os.WIFSIGNALED(code):
1107 elif os.WIFSIGNALED(code):
1102 val = os.WTERMSIG(code)
1108 val = os.WTERMSIG(code)
1103 return _("killed by signal %d") % val, val
1109 return _("killed by signal %d") % val, val
1104 elif os.WIFSTOPPED(code):
1110 elif os.WIFSTOPPED(code):
1105 val = os.WSTOPSIG(code)
1111 val = os.WSTOPSIG(code)
1106 return _("stopped by signal %d") % val, val
1112 return _("stopped by signal %d") % val, val
1107 raise ValueError(_("invalid exit code"))
1113 raise ValueError(_("invalid exit code"))
1108
1114
1109 def isowner(fp, st=None):
1115 def isowner(fp, st=None):
1110 """Return True if the file object f belongs to the current user.
1116 """Return True if the file object f belongs to the current user.
1111
1117
1112 The return value of a util.fstat(f) may be passed as the st argument.
1118 The return value of a util.fstat(f) may be passed as the st argument.
1113 """
1119 """
1114 if st is None:
1120 if st is None:
1115 st = fstat(fp)
1121 st = fstat(fp)
1116 return st.st_uid == os.getuid()
1122 return st.st_uid == os.getuid()
1117
1123
1118 def find_in_path(name, path, default=None):
1124 def find_in_path(name, path, default=None):
1119 '''find name in search path. path can be string (will be split
1125 '''find name in search path. path can be string (will be split
1120 with os.pathsep), or iterable thing that returns strings. if name
1126 with os.pathsep), or iterable thing that returns strings. if name
1121 found, return path to name. else return default.'''
1127 found, return path to name. else return default.'''
1122 if isinstance(path, str):
1128 if isinstance(path, str):
1123 path = path.split(os.pathsep)
1129 path = path.split(os.pathsep)
1124 for p in path:
1130 for p in path:
1125 p_name = os.path.join(p, name)
1131 p_name = os.path.join(p, name)
1126 if os.path.exists(p_name):
1132 if os.path.exists(p_name):
1127 return p_name
1133 return p_name
1128 return default
1134 return default
1129
1135
1130 def set_signal_handler():
1136 def set_signal_handler():
1131 pass
1137 pass
1132
1138
1133 def find_exe(name, default=None):
1139 def find_exe(name, default=None):
1134 '''find path of an executable.
1140 '''find path of an executable.
1135 if name contains a path component, return it as is. otherwise,
1141 if name contains a path component, return it as is. otherwise,
1136 use normal executable search path.'''
1142 use normal executable search path.'''
1137
1143
1138 if os.sep in name or sys.platform == 'OpenVMS':
1144 if os.sep in name or sys.platform == 'OpenVMS':
1139 # don't check the executable bit. if the file isn't
1145 # don't check the executable bit. if the file isn't
1140 # executable, whoever tries to actually run it will give a
1146 # executable, whoever tries to actually run it will give a
1141 # much more useful error message.
1147 # much more useful error message.
1142 return name
1148 return name
1143 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1149 return find_in_path(name, os.environ.get('PATH', ''), default=default)
1144
1150
1145 def _buildencodefun():
1151 def _buildencodefun():
1146 e = '_'
1152 e = '_'
1147 win_reserved = [ord(x) for x in '\\:*?"<>|']
1153 win_reserved = [ord(x) for x in '\\:*?"<>|']
1148 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1154 cmap = dict([ (chr(x), chr(x)) for x in xrange(127) ])
1149 for x in (range(32) + range(126, 256) + win_reserved):
1155 for x in (range(32) + range(126, 256) + win_reserved):
1150 cmap[chr(x)] = "~%02x" % x
1156 cmap[chr(x)] = "~%02x" % x
1151 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1157 for x in range(ord("A"), ord("Z")+1) + [ord(e)]:
1152 cmap[chr(x)] = e + chr(x).lower()
1158 cmap[chr(x)] = e + chr(x).lower()
1153 dmap = {}
1159 dmap = {}
1154 for k, v in cmap.iteritems():
1160 for k, v in cmap.iteritems():
1155 dmap[v] = k
1161 dmap[v] = k
1156 def decode(s):
1162 def decode(s):
1157 i = 0
1163 i = 0
1158 while i < len(s):
1164 while i < len(s):
1159 for l in xrange(1, 4):
1165 for l in xrange(1, 4):
1160 try:
1166 try:
1161 yield dmap[s[i:i+l]]
1167 yield dmap[s[i:i+l]]
1162 i += l
1168 i += l
1163 break
1169 break
1164 except KeyError:
1170 except KeyError:
1165 pass
1171 pass
1166 else:
1172 else:
1167 raise KeyError
1173 raise KeyError
1168 return (lambda s: "".join([cmap[c] for c in s]),
1174 return (lambda s: "".join([cmap[c] for c in s]),
1169 lambda s: "".join(list(decode(s))))
1175 lambda s: "".join(list(decode(s))))
1170
1176
1171 encodefilename, decodefilename = _buildencodefun()
1177 encodefilename, decodefilename = _buildencodefun()
1172
1178
1173 def encodedopener(openerfn, fn):
1179 def encodedopener(openerfn, fn):
1174 def o(path, *args, **kw):
1180 def o(path, *args, **kw):
1175 return openerfn(fn(path), *args, **kw)
1181 return openerfn(fn(path), *args, **kw)
1176 return o
1182 return o
1177
1183
1178 def opener(base, audit=True):
1184 def opener(base, audit=True):
1179 """
1185 """
1180 return a function that opens files relative to base
1186 return a function that opens files relative to base
1181
1187
1182 this function is used to hide the details of COW semantics and
1188 this function is used to hide the details of COW semantics and
1183 remote file access from higher level code.
1189 remote file access from higher level code.
1184 """
1190 """
1185 def mktempcopy(name, emptyok=False):
1191 def mktempcopy(name, emptyok=False):
1186 d, fn = os.path.split(name)
1192 d, fn = os.path.split(name)
1187 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1193 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
1188 os.close(fd)
1194 os.close(fd)
1189 # Temporary files are created with mode 0600, which is usually not
1195 # Temporary files are created with mode 0600, which is usually not
1190 # what we want. If the original file already exists, just copy
1196 # what we want. If the original file already exists, just copy
1191 # its mode. Otherwise, manually obey umask.
1197 # its mode. Otherwise, manually obey umask.
1192 try:
1198 try:
1193 st_mode = os.lstat(name).st_mode
1199 st_mode = os.lstat(name).st_mode
1194 except OSError, inst:
1200 except OSError, inst:
1195 if inst.errno != errno.ENOENT:
1201 if inst.errno != errno.ENOENT:
1196 raise
1202 raise
1197 st_mode = 0666 & ~_umask
1203 st_mode = 0666 & ~_umask
1198 os.chmod(temp, st_mode)
1204 os.chmod(temp, st_mode)
1199 if emptyok:
1205 if emptyok:
1200 return temp
1206 return temp
1201 try:
1207 try:
1202 try:
1208 try:
1203 ifp = posixfile(name, "rb")
1209 ifp = posixfile(name, "rb")
1204 except IOError, inst:
1210 except IOError, inst:
1205 if inst.errno == errno.ENOENT:
1211 if inst.errno == errno.ENOENT:
1206 return temp
1212 return temp
1207 if not getattr(inst, 'filename', None):
1213 if not getattr(inst, 'filename', None):
1208 inst.filename = name
1214 inst.filename = name
1209 raise
1215 raise
1210 ofp = posixfile(temp, "wb")
1216 ofp = posixfile(temp, "wb")
1211 for chunk in filechunkiter(ifp):
1217 for chunk in filechunkiter(ifp):
1212 ofp.write(chunk)
1218 ofp.write(chunk)
1213 ifp.close()
1219 ifp.close()
1214 ofp.close()
1220 ofp.close()
1215 except:
1221 except:
1216 try: os.unlink(temp)
1222 try: os.unlink(temp)
1217 except: pass
1223 except: pass
1218 raise
1224 raise
1219 return temp
1225 return temp
1220
1226
1221 class atomictempfile(posixfile):
1227 class atomictempfile(posixfile):
1222 """the file will only be copied when rename is called"""
1228 """the file will only be copied when rename is called"""
1223 def __init__(self, name, mode):
1229 def __init__(self, name, mode):
1224 self.__name = name
1230 self.__name = name
1225 self.temp = mktempcopy(name, emptyok=('w' in mode))
1231 self.temp = mktempcopy(name, emptyok=('w' in mode))
1226 posixfile.__init__(self, self.temp, mode)
1232 posixfile.__init__(self, self.temp, mode)
1227 def rename(self):
1233 def rename(self):
1228 if not self.closed:
1234 if not self.closed:
1229 posixfile.close(self)
1235 posixfile.close(self)
1230 rename(self.temp, localpath(self.__name))
1236 rename(self.temp, localpath(self.__name))
1231 def __del__(self):
1237 def __del__(self):
1232 if not self.closed:
1238 if not self.closed:
1233 try:
1239 try:
1234 os.unlink(self.temp)
1240 os.unlink(self.temp)
1235 except: pass
1241 except: pass
1236 posixfile.close(self)
1242 posixfile.close(self)
1237
1243
1238 def o(path, mode="r", text=False, atomictemp=False):
1244 def o(path, mode="r", text=False, atomictemp=False):
1239 if audit:
1245 if audit:
1240 audit_path(path)
1246 audit_path(path)
1241 f = os.path.join(base, path)
1247 f = os.path.join(base, path)
1242
1248
1243 if not text and "b" not in mode:
1249 if not text and "b" not in mode:
1244 mode += "b" # for that other OS
1250 mode += "b" # for that other OS
1245
1251
1246 if mode[0] != "r":
1252 if mode[0] != "r":
1247 try:
1253 try:
1248 nlink = nlinks(f)
1254 nlink = nlinks(f)
1249 except OSError:
1255 except OSError:
1250 nlink = 0
1256 nlink = 0
1251 d = os.path.dirname(f)
1257 d = os.path.dirname(f)
1252 if not os.path.isdir(d):
1258 if not os.path.isdir(d):
1253 os.makedirs(d)
1259 os.makedirs(d)
1254 if atomictemp:
1260 if atomictemp:
1255 return atomictempfile(f, mode)
1261 return atomictempfile(f, mode)
1256 if nlink > 1:
1262 if nlink > 1:
1257 rename(mktempcopy(f), f)
1263 rename(mktempcopy(f), f)
1258 return posixfile(f, mode)
1264 return posixfile(f, mode)
1259
1265
1260 return o
1266 return o
1261
1267
1262 class chunkbuffer(object):
1268 class chunkbuffer(object):
1263 """Allow arbitrary sized chunks of data to be efficiently read from an
1269 """Allow arbitrary sized chunks of data to be efficiently read from an
1264 iterator over chunks of arbitrary size."""
1270 iterator over chunks of arbitrary size."""
1265
1271
1266 def __init__(self, in_iter, targetsize = 2**16):
1272 def __init__(self, in_iter, targetsize = 2**16):
1267 """in_iter is the iterator that's iterating over the input chunks.
1273 """in_iter is the iterator that's iterating over the input chunks.
1268 targetsize is how big a buffer to try to maintain."""
1274 targetsize is how big a buffer to try to maintain."""
1269 self.in_iter = iter(in_iter)
1275 self.in_iter = iter(in_iter)
1270 self.buf = ''
1276 self.buf = ''
1271 self.targetsize = int(targetsize)
1277 self.targetsize = int(targetsize)
1272 if self.targetsize <= 0:
1278 if self.targetsize <= 0:
1273 raise ValueError(_("targetsize must be greater than 0, was %d") %
1279 raise ValueError(_("targetsize must be greater than 0, was %d") %
1274 targetsize)
1280 targetsize)
1275 self.iterempty = False
1281 self.iterempty = False
1276
1282
1277 def fillbuf(self):
1283 def fillbuf(self):
1278 """Ignore target size; read every chunk from iterator until empty."""
1284 """Ignore target size; read every chunk from iterator until empty."""
1279 if not self.iterempty:
1285 if not self.iterempty:
1280 collector = cStringIO.StringIO()
1286 collector = cStringIO.StringIO()
1281 collector.write(self.buf)
1287 collector.write(self.buf)
1282 for ch in self.in_iter:
1288 for ch in self.in_iter:
1283 collector.write(ch)
1289 collector.write(ch)
1284 self.buf = collector.getvalue()
1290 self.buf = collector.getvalue()
1285 self.iterempty = True
1291 self.iterempty = True
1286
1292
1287 def read(self, l):
1293 def read(self, l):
1288 """Read L bytes of data from the iterator of chunks of data.
1294 """Read L bytes of data from the iterator of chunks of data.
1289 Returns less than L bytes if the iterator runs dry."""
1295 Returns less than L bytes if the iterator runs dry."""
1290 if l > len(self.buf) and not self.iterempty:
1296 if l > len(self.buf) and not self.iterempty:
1291 # Clamp to a multiple of self.targetsize
1297 # Clamp to a multiple of self.targetsize
1292 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1298 targetsize = self.targetsize * ((l // self.targetsize) + 1)
1293 collector = cStringIO.StringIO()
1299 collector = cStringIO.StringIO()
1294 collector.write(self.buf)
1300 collector.write(self.buf)
1295 collected = len(self.buf)
1301 collected = len(self.buf)
1296 for chunk in self.in_iter:
1302 for chunk in self.in_iter:
1297 collector.write(chunk)
1303 collector.write(chunk)
1298 collected += len(chunk)
1304 collected += len(chunk)
1299 if collected >= targetsize:
1305 if collected >= targetsize:
1300 break
1306 break
1301 if collected < targetsize:
1307 if collected < targetsize:
1302 self.iterempty = True
1308 self.iterempty = True
1303 self.buf = collector.getvalue()
1309 self.buf = collector.getvalue()
1304 s, self.buf = self.buf[:l], buffer(self.buf, l)
1310 s, self.buf = self.buf[:l], buffer(self.buf, l)
1305 return s
1311 return s
1306
1312
1307 def filechunkiter(f, size=65536, limit=None):
1313 def filechunkiter(f, size=65536, limit=None):
1308 """Create a generator that produces the data in the file size
1314 """Create a generator that produces the data in the file size
1309 (default 65536) bytes at a time, up to optional limit (default is
1315 (default 65536) bytes at a time, up to optional limit (default is
1310 to read all data). Chunks may be less than size bytes if the
1316 to read all data). Chunks may be less than size bytes if the
1311 chunk is the last chunk in the file, or the file is a socket or
1317 chunk is the last chunk in the file, or the file is a socket or
1312 some other type of file that sometimes reads less data than is
1318 some other type of file that sometimes reads less data than is
1313 requested."""
1319 requested."""
1314 assert size >= 0
1320 assert size >= 0
1315 assert limit is None or limit >= 0
1321 assert limit is None or limit >= 0
1316 while True:
1322 while True:
1317 if limit is None: nbytes = size
1323 if limit is None: nbytes = size
1318 else: nbytes = min(limit, size)
1324 else: nbytes = min(limit, size)
1319 s = nbytes and f.read(nbytes)
1325 s = nbytes and f.read(nbytes)
1320 if not s: break
1326 if not s: break
1321 if limit: limit -= len(s)
1327 if limit: limit -= len(s)
1322 yield s
1328 yield s
1323
1329
1324 def makedate():
1330 def makedate():
1325 lt = time.localtime()
1331 lt = time.localtime()
1326 if lt[8] == 1 and time.daylight:
1332 if lt[8] == 1 and time.daylight:
1327 tz = time.altzone
1333 tz = time.altzone
1328 else:
1334 else:
1329 tz = time.timezone
1335 tz = time.timezone
1330 return time.mktime(lt), tz
1336 return time.mktime(lt), tz
1331
1337
1332 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1338 def datestr(date=None, format='%a %b %d %H:%M:%S %Y', timezone=True):
1333 """represent a (unixtime, offset) tuple as a localized time.
1339 """represent a (unixtime, offset) tuple as a localized time.
1334 unixtime is seconds since the epoch, and offset is the time zone's
1340 unixtime is seconds since the epoch, and offset is the time zone's
1335 number of seconds away from UTC. if timezone is false, do not
1341 number of seconds away from UTC. if timezone is false, do not
1336 append time zone to string."""
1342 append time zone to string."""
1337 t, tz = date or makedate()
1343 t, tz = date or makedate()
1338 s = time.strftime(format, time.gmtime(float(t) - tz))
1344 s = time.strftime(format, time.gmtime(float(t) - tz))
1339 if timezone:
1345 if timezone:
1340 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1346 s += " %+03d%02d" % (-tz / 3600, ((-tz % 3600) / 60))
1341 return s
1347 return s
1342
1348
1343 def strdate(string, format, defaults):
1349 def strdate(string, format, defaults):
1344 """parse a localized time string and return a (unixtime, offset) tuple.
1350 """parse a localized time string and return a (unixtime, offset) tuple.
1345 if the string cannot be parsed, ValueError is raised."""
1351 if the string cannot be parsed, ValueError is raised."""
1346 def timezone(string):
1352 def timezone(string):
1347 tz = string.split()[-1]
1353 tz = string.split()[-1]
1348 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1354 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
1349 tz = int(tz)
1355 tz = int(tz)
1350 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1356 offset = - 3600 * (tz / 100) - 60 * (tz % 100)
1351 return offset
1357 return offset
1352 if tz == "GMT" or tz == "UTC":
1358 if tz == "GMT" or tz == "UTC":
1353 return 0
1359 return 0
1354 return None
1360 return None
1355
1361
1356 # NOTE: unixtime = localunixtime + offset
1362 # NOTE: unixtime = localunixtime + offset
1357 offset, date = timezone(string), string
1363 offset, date = timezone(string), string
1358 if offset != None:
1364 if offset != None:
1359 date = " ".join(string.split()[:-1])
1365 date = " ".join(string.split()[:-1])
1360
1366
1361 # add missing elements from defaults
1367 # add missing elements from defaults
1362 for part in defaults:
1368 for part in defaults:
1363 found = [True for p in part if ("%"+p) in format]
1369 found = [True for p in part if ("%"+p) in format]
1364 if not found:
1370 if not found:
1365 date += "@" + defaults[part]
1371 date += "@" + defaults[part]
1366 format += "@%" + part[0]
1372 format += "@%" + part[0]
1367
1373
1368 timetuple = time.strptime(date, format)
1374 timetuple = time.strptime(date, format)
1369 localunixtime = int(calendar.timegm(timetuple))
1375 localunixtime = int(calendar.timegm(timetuple))
1370 if offset is None:
1376 if offset is None:
1371 # local timezone
1377 # local timezone
1372 unixtime = int(time.mktime(timetuple))
1378 unixtime = int(time.mktime(timetuple))
1373 offset = unixtime - localunixtime
1379 offset = unixtime - localunixtime
1374 else:
1380 else:
1375 unixtime = localunixtime + offset
1381 unixtime = localunixtime + offset
1376 return unixtime, offset
1382 return unixtime, offset
1377
1383
1378 def parsedate(string, formats=None, defaults=None):
1384 def parsedate(string, formats=None, defaults=None):
1379 """parse a localized time string and return a (unixtime, offset) tuple.
1385 """parse a localized time string and return a (unixtime, offset) tuple.
1380 The date may be a "unixtime offset" string or in one of the specified
1386 The date may be a "unixtime offset" string or in one of the specified
1381 formats."""
1387 formats."""
1382 if not string:
1388 if not string:
1383 return 0, 0
1389 return 0, 0
1384 if not formats:
1390 if not formats:
1385 formats = defaultdateformats
1391 formats = defaultdateformats
1386 string = string.strip()
1392 string = string.strip()
1387 try:
1393 try:
1388 when, offset = map(int, string.split(' '))
1394 when, offset = map(int, string.split(' '))
1389 except ValueError:
1395 except ValueError:
1390 # fill out defaults
1396 # fill out defaults
1391 if not defaults:
1397 if not defaults:
1392 defaults = {}
1398 defaults = {}
1393 now = makedate()
1399 now = makedate()
1394 for part in "d mb yY HI M S".split():
1400 for part in "d mb yY HI M S".split():
1395 if part not in defaults:
1401 if part not in defaults:
1396 if part[0] in "HMS":
1402 if part[0] in "HMS":
1397 defaults[part] = "00"
1403 defaults[part] = "00"
1398 elif part[0] in "dm":
1404 elif part[0] in "dm":
1399 defaults[part] = "1"
1405 defaults[part] = "1"
1400 else:
1406 else:
1401 defaults[part] = datestr(now, "%" + part[0], False)
1407 defaults[part] = datestr(now, "%" + part[0], False)
1402
1408
1403 for format in formats:
1409 for format in formats:
1404 try:
1410 try:
1405 when, offset = strdate(string, format, defaults)
1411 when, offset = strdate(string, format, defaults)
1406 except ValueError:
1412 except ValueError:
1407 pass
1413 pass
1408 else:
1414 else:
1409 break
1415 break
1410 else:
1416 else:
1411 raise Abort(_('invalid date: %r ') % string)
1417 raise Abort(_('invalid date: %r ') % string)
1412 # validate explicit (probably user-specified) date and
1418 # validate explicit (probably user-specified) date and
1413 # time zone offset. values must fit in signed 32 bits for
1419 # time zone offset. values must fit in signed 32 bits for
1414 # current 32-bit linux runtimes. timezones go from UTC-12
1420 # current 32-bit linux runtimes. timezones go from UTC-12
1415 # to UTC+14
1421 # to UTC+14
1416 if abs(when) > 0x7fffffff:
1422 if abs(when) > 0x7fffffff:
1417 raise Abort(_('date exceeds 32 bits: %d') % when)
1423 raise Abort(_('date exceeds 32 bits: %d') % when)
1418 if offset < -50400 or offset > 43200:
1424 if offset < -50400 or offset > 43200:
1419 raise Abort(_('impossible time zone offset: %d') % offset)
1425 raise Abort(_('impossible time zone offset: %d') % offset)
1420 return when, offset
1426 return when, offset
1421
1427
1422 def matchdate(date):
1428 def matchdate(date):
1423 """Return a function that matches a given date match specifier
1429 """Return a function that matches a given date match specifier
1424
1430
1425 Formats include:
1431 Formats include:
1426
1432
1427 '{date}' match a given date to the accuracy provided
1433 '{date}' match a given date to the accuracy provided
1428
1434
1429 '<{date}' on or before a given date
1435 '<{date}' on or before a given date
1430
1436
1431 '>{date}' on or after a given date
1437 '>{date}' on or after a given date
1432
1438
1433 """
1439 """
1434
1440
1435 def lower(date):
1441 def lower(date):
1436 return parsedate(date, extendeddateformats)[0]
1442 return parsedate(date, extendeddateformats)[0]
1437
1443
1438 def upper(date):
1444 def upper(date):
1439 d = dict(mb="12", HI="23", M="59", S="59")
1445 d = dict(mb="12", HI="23", M="59", S="59")
1440 for days in "31 30 29".split():
1446 for days in "31 30 29".split():
1441 try:
1447 try:
1442 d["d"] = days
1448 d["d"] = days
1443 return parsedate(date, extendeddateformats, d)[0]
1449 return parsedate(date, extendeddateformats, d)[0]
1444 except:
1450 except:
1445 pass
1451 pass
1446 d["d"] = "28"
1452 d["d"] = "28"
1447 return parsedate(date, extendeddateformats, d)[0]
1453 return parsedate(date, extendeddateformats, d)[0]
1448
1454
1449 if date[0] == "<":
1455 if date[0] == "<":
1450 when = upper(date[1:])
1456 when = upper(date[1:])
1451 return lambda x: x <= when
1457 return lambda x: x <= when
1452 elif date[0] == ">":
1458 elif date[0] == ">":
1453 when = lower(date[1:])
1459 when = lower(date[1:])
1454 return lambda x: x >= when
1460 return lambda x: x >= when
1455 elif date[0] == "-":
1461 elif date[0] == "-":
1456 try:
1462 try:
1457 days = int(date[1:])
1463 days = int(date[1:])
1458 except ValueError:
1464 except ValueError:
1459 raise Abort(_("invalid day spec: %s") % date[1:])
1465 raise Abort(_("invalid day spec: %s") % date[1:])
1460 when = makedate()[0] - days * 3600 * 24
1466 when = makedate()[0] - days * 3600 * 24
1461 return lambda x: x >= when
1467 return lambda x: x >= when
1462 elif " to " in date:
1468 elif " to " in date:
1463 a, b = date.split(" to ")
1469 a, b = date.split(" to ")
1464 start, stop = lower(a), upper(b)
1470 start, stop = lower(a), upper(b)
1465 return lambda x: x >= start and x <= stop
1471 return lambda x: x >= start and x <= stop
1466 else:
1472 else:
1467 start, stop = lower(date), upper(date)
1473 start, stop = lower(date), upper(date)
1468 return lambda x: x >= start and x <= stop
1474 return lambda x: x >= start and x <= stop
1469
1475
1470 def shortuser(user):
1476 def shortuser(user):
1471 """Return a short representation of a user name or email address."""
1477 """Return a short representation of a user name or email address."""
1472 f = user.find('@')
1478 f = user.find('@')
1473 if f >= 0:
1479 if f >= 0:
1474 user = user[:f]
1480 user = user[:f]
1475 f = user.find('<')
1481 f = user.find('<')
1476 if f >= 0:
1482 if f >= 0:
1477 user = user[f+1:]
1483 user = user[f+1:]
1478 f = user.find(' ')
1484 f = user.find(' ')
1479 if f >= 0:
1485 if f >= 0:
1480 user = user[:f]
1486 user = user[:f]
1481 f = user.find('.')
1487 f = user.find('.')
1482 if f >= 0:
1488 if f >= 0:
1483 user = user[:f]
1489 user = user[:f]
1484 return user
1490 return user
1485
1491
1486 def ellipsis(text, maxlength=400):
1492 def ellipsis(text, maxlength=400):
1487 """Trim string to at most maxlength (default: 400) characters."""
1493 """Trim string to at most maxlength (default: 400) characters."""
1488 if len(text) <= maxlength:
1494 if len(text) <= maxlength:
1489 return text
1495 return text
1490 else:
1496 else:
1491 return "%s..." % (text[:maxlength-3])
1497 return "%s..." % (text[:maxlength-3])
1492
1498
1493 def walkrepos(path):
1499 def walkrepos(path):
1494 '''yield every hg repository under path, recursively.'''
1500 '''yield every hg repository under path, recursively.'''
1495 def errhandler(err):
1501 def errhandler(err):
1496 if err.filename == path:
1502 if err.filename == path:
1497 raise err
1503 raise err
1498
1504
1499 for root, dirs, files in os.walk(path, onerror=errhandler):
1505 for root, dirs, files in os.walk(path, onerror=errhandler):
1500 for d in dirs:
1506 for d in dirs:
1501 if d == '.hg':
1507 if d == '.hg':
1502 yield root
1508 yield root
1503 dirs[:] = []
1509 dirs[:] = []
1504 break
1510 break
1505
1511
1506 _rcpath = None
1512 _rcpath = None
1507
1513
1508 def os_rcpath():
1514 def os_rcpath():
1509 '''return default os-specific hgrc search path'''
1515 '''return default os-specific hgrc search path'''
1510 path = system_rcpath()
1516 path = system_rcpath()
1511 path.extend(user_rcpath())
1517 path.extend(user_rcpath())
1512 path = [os.path.normpath(f) for f in path]
1518 path = [os.path.normpath(f) for f in path]
1513 return path
1519 return path
1514
1520
1515 def rcpath():
1521 def rcpath():
1516 '''return hgrc search path. if env var HGRCPATH is set, use it.
1522 '''return hgrc search path. if env var HGRCPATH is set, use it.
1517 for each item in path, if directory, use files ending in .rc,
1523 for each item in path, if directory, use files ending in .rc,
1518 else use item.
1524 else use item.
1519 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1525 make HGRCPATH empty to only look in .hg/hgrc of current repo.
1520 if no HGRCPATH, use default os-specific path.'''
1526 if no HGRCPATH, use default os-specific path.'''
1521 global _rcpath
1527 global _rcpath
1522 if _rcpath is None:
1528 if _rcpath is None:
1523 if 'HGRCPATH' in os.environ:
1529 if 'HGRCPATH' in os.environ:
1524 _rcpath = []
1530 _rcpath = []
1525 for p in os.environ['HGRCPATH'].split(os.pathsep):
1531 for p in os.environ['HGRCPATH'].split(os.pathsep):
1526 if not p: continue
1532 if not p: continue
1527 if os.path.isdir(p):
1533 if os.path.isdir(p):
1528 for f in os.listdir(p):
1534 for f in os.listdir(p):
1529 if f.endswith('.rc'):
1535 if f.endswith('.rc'):
1530 _rcpath.append(os.path.join(p, f))
1536 _rcpath.append(os.path.join(p, f))
1531 else:
1537 else:
1532 _rcpath.append(p)
1538 _rcpath.append(p)
1533 else:
1539 else:
1534 _rcpath = os_rcpath()
1540 _rcpath = os_rcpath()
1535 return _rcpath
1541 return _rcpath
1536
1542
1537 def bytecount(nbytes):
1543 def bytecount(nbytes):
1538 '''return byte count formatted as readable string, with units'''
1544 '''return byte count formatted as readable string, with units'''
1539
1545
1540 units = (
1546 units = (
1541 (100, 1<<30, _('%.0f GB')),
1547 (100, 1<<30, _('%.0f GB')),
1542 (10, 1<<30, _('%.1f GB')),
1548 (10, 1<<30, _('%.1f GB')),
1543 (1, 1<<30, _('%.2f GB')),
1549 (1, 1<<30, _('%.2f GB')),
1544 (100, 1<<20, _('%.0f MB')),
1550 (100, 1<<20, _('%.0f MB')),
1545 (10, 1<<20, _('%.1f MB')),
1551 (10, 1<<20, _('%.1f MB')),
1546 (1, 1<<20, _('%.2f MB')),
1552 (1, 1<<20, _('%.2f MB')),
1547 (100, 1<<10, _('%.0f KB')),
1553 (100, 1<<10, _('%.0f KB')),
1548 (10, 1<<10, _('%.1f KB')),
1554 (10, 1<<10, _('%.1f KB')),
1549 (1, 1<<10, _('%.2f KB')),
1555 (1, 1<<10, _('%.2f KB')),
1550 (1, 1, _('%.0f bytes')),
1556 (1, 1, _('%.0f bytes')),
1551 )
1557 )
1552
1558
1553 for multiplier, divisor, format in units:
1559 for multiplier, divisor, format in units:
1554 if nbytes >= divisor * multiplier:
1560 if nbytes >= divisor * multiplier:
1555 return format % (nbytes / float(divisor))
1561 return format % (nbytes / float(divisor))
1556 return units[-1][2] % nbytes
1562 return units[-1][2] % nbytes
1557
1563
1558 def drop_scheme(scheme, path):
1564 def drop_scheme(scheme, path):
1559 sc = scheme + ':'
1565 sc = scheme + ':'
1560 if path.startswith(sc):
1566 if path.startswith(sc):
1561 path = path[len(sc):]
1567 path = path[len(sc):]
1562 if path.startswith('//'):
1568 if path.startswith('//'):
1563 path = path[2:]
1569 path = path[2:]
1564 return path
1570 return path
@@ -1,310 +1,310
1 # util_win32.py - utility functions that use win32 API
1 # util_win32.py - utility functions that use win32 API
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of
6 # This software may be used and distributed according to the terms of
7 # the GNU General Public License, incorporated herein by reference.
7 # the GNU General Public License, incorporated herein by reference.
8
8
9 # Mark Hammond's win32all package allows better functionality on
9 # Mark Hammond's win32all package allows better functionality on
10 # Windows. this module overrides definitions in util.py. if not
10 # Windows. this module overrides definitions in util.py. if not
11 # available, import of this module will fail, and generic code will be
11 # available, import of this module will fail, and generic code will be
12 # used.
12 # used.
13
13
14 import win32api
14 import win32api
15
15
16 from i18n import _
16 from i18n import _
17 import errno, os, pywintypes, win32con, win32file, win32process
17 import errno, os, pywintypes, win32con, win32file, win32process
18 import cStringIO, winerror
18 import cStringIO, winerror
19 from win32com.shell import shell,shellcon
19 from win32com.shell import shell,shellcon
20
20
21 class WinError:
21 class WinError:
22 winerror_map = {
22 winerror_map = {
23 winerror.ERROR_ACCESS_DENIED: errno.EACCES,
23 winerror.ERROR_ACCESS_DENIED: errno.EACCES,
24 winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES,
24 winerror.ERROR_ACCOUNT_DISABLED: errno.EACCES,
25 winerror.ERROR_ACCOUNT_RESTRICTION: errno.EACCES,
25 winerror.ERROR_ACCOUNT_RESTRICTION: errno.EACCES,
26 winerror.ERROR_ALREADY_ASSIGNED: errno.EBUSY,
26 winerror.ERROR_ALREADY_ASSIGNED: errno.EBUSY,
27 winerror.ERROR_ALREADY_EXISTS: errno.EEXIST,
27 winerror.ERROR_ALREADY_EXISTS: errno.EEXIST,
28 winerror.ERROR_ARITHMETIC_OVERFLOW: errno.ERANGE,
28 winerror.ERROR_ARITHMETIC_OVERFLOW: errno.ERANGE,
29 winerror.ERROR_BAD_COMMAND: errno.EIO,
29 winerror.ERROR_BAD_COMMAND: errno.EIO,
30 winerror.ERROR_BAD_DEVICE: errno.ENODEV,
30 winerror.ERROR_BAD_DEVICE: errno.ENODEV,
31 winerror.ERROR_BAD_DRIVER_LEVEL: errno.ENXIO,
31 winerror.ERROR_BAD_DRIVER_LEVEL: errno.ENXIO,
32 winerror.ERROR_BAD_EXE_FORMAT: errno.ENOEXEC,
32 winerror.ERROR_BAD_EXE_FORMAT: errno.ENOEXEC,
33 winerror.ERROR_BAD_FORMAT: errno.ENOEXEC,
33 winerror.ERROR_BAD_FORMAT: errno.ENOEXEC,
34 winerror.ERROR_BAD_LENGTH: errno.EINVAL,
34 winerror.ERROR_BAD_LENGTH: errno.EINVAL,
35 winerror.ERROR_BAD_PATHNAME: errno.ENOENT,
35 winerror.ERROR_BAD_PATHNAME: errno.ENOENT,
36 winerror.ERROR_BAD_PIPE: errno.EPIPE,
36 winerror.ERROR_BAD_PIPE: errno.EPIPE,
37 winerror.ERROR_BAD_UNIT: errno.ENODEV,
37 winerror.ERROR_BAD_UNIT: errno.ENODEV,
38 winerror.ERROR_BAD_USERNAME: errno.EINVAL,
38 winerror.ERROR_BAD_USERNAME: errno.EINVAL,
39 winerror.ERROR_BROKEN_PIPE: errno.EPIPE,
39 winerror.ERROR_BROKEN_PIPE: errno.EPIPE,
40 winerror.ERROR_BUFFER_OVERFLOW: errno.ENAMETOOLONG,
40 winerror.ERROR_BUFFER_OVERFLOW: errno.ENAMETOOLONG,
41 winerror.ERROR_BUSY: errno.EBUSY,
41 winerror.ERROR_BUSY: errno.EBUSY,
42 winerror.ERROR_BUSY_DRIVE: errno.EBUSY,
42 winerror.ERROR_BUSY_DRIVE: errno.EBUSY,
43 winerror.ERROR_CALL_NOT_IMPLEMENTED: errno.ENOSYS,
43 winerror.ERROR_CALL_NOT_IMPLEMENTED: errno.ENOSYS,
44 winerror.ERROR_CANNOT_MAKE: errno.EACCES,
44 winerror.ERROR_CANNOT_MAKE: errno.EACCES,
45 winerror.ERROR_CANTOPEN: errno.EIO,
45 winerror.ERROR_CANTOPEN: errno.EIO,
46 winerror.ERROR_CANTREAD: errno.EIO,
46 winerror.ERROR_CANTREAD: errno.EIO,
47 winerror.ERROR_CANTWRITE: errno.EIO,
47 winerror.ERROR_CANTWRITE: errno.EIO,
48 winerror.ERROR_CRC: errno.EIO,
48 winerror.ERROR_CRC: errno.EIO,
49 winerror.ERROR_CURRENT_DIRECTORY: errno.EACCES,
49 winerror.ERROR_CURRENT_DIRECTORY: errno.EACCES,
50 winerror.ERROR_DEVICE_IN_USE: errno.EBUSY,
50 winerror.ERROR_DEVICE_IN_USE: errno.EBUSY,
51 winerror.ERROR_DEV_NOT_EXIST: errno.ENODEV,
51 winerror.ERROR_DEV_NOT_EXIST: errno.ENODEV,
52 winerror.ERROR_DIRECTORY: errno.EINVAL,
52 winerror.ERROR_DIRECTORY: errno.EINVAL,
53 winerror.ERROR_DIR_NOT_EMPTY: errno.ENOTEMPTY,
53 winerror.ERROR_DIR_NOT_EMPTY: errno.ENOTEMPTY,
54 winerror.ERROR_DISK_CHANGE: errno.EIO,
54 winerror.ERROR_DISK_CHANGE: errno.EIO,
55 winerror.ERROR_DISK_FULL: errno.ENOSPC,
55 winerror.ERROR_DISK_FULL: errno.ENOSPC,
56 winerror.ERROR_DRIVE_LOCKED: errno.EBUSY,
56 winerror.ERROR_DRIVE_LOCKED: errno.EBUSY,
57 winerror.ERROR_ENVVAR_NOT_FOUND: errno.EINVAL,
57 winerror.ERROR_ENVVAR_NOT_FOUND: errno.EINVAL,
58 winerror.ERROR_EXE_MARKED_INVALID: errno.ENOEXEC,
58 winerror.ERROR_EXE_MARKED_INVALID: errno.ENOEXEC,
59 winerror.ERROR_FILENAME_EXCED_RANGE: errno.ENAMETOOLONG,
59 winerror.ERROR_FILENAME_EXCED_RANGE: errno.ENAMETOOLONG,
60 winerror.ERROR_FILE_EXISTS: errno.EEXIST,
60 winerror.ERROR_FILE_EXISTS: errno.EEXIST,
61 winerror.ERROR_FILE_INVALID: errno.ENODEV,
61 winerror.ERROR_FILE_INVALID: errno.ENODEV,
62 winerror.ERROR_FILE_NOT_FOUND: errno.ENOENT,
62 winerror.ERROR_FILE_NOT_FOUND: errno.ENOENT,
63 winerror.ERROR_GEN_FAILURE: errno.EIO,
63 winerror.ERROR_GEN_FAILURE: errno.EIO,
64 winerror.ERROR_HANDLE_DISK_FULL: errno.ENOSPC,
64 winerror.ERROR_HANDLE_DISK_FULL: errno.ENOSPC,
65 winerror.ERROR_INSUFFICIENT_BUFFER: errno.ENOMEM,
65 winerror.ERROR_INSUFFICIENT_BUFFER: errno.ENOMEM,
66 winerror.ERROR_INVALID_ACCESS: errno.EACCES,
66 winerror.ERROR_INVALID_ACCESS: errno.EACCES,
67 winerror.ERROR_INVALID_ADDRESS: errno.EFAULT,
67 winerror.ERROR_INVALID_ADDRESS: errno.EFAULT,
68 winerror.ERROR_INVALID_BLOCK: errno.EFAULT,
68 winerror.ERROR_INVALID_BLOCK: errno.EFAULT,
69 winerror.ERROR_INVALID_DATA: errno.EINVAL,
69 winerror.ERROR_INVALID_DATA: errno.EINVAL,
70 winerror.ERROR_INVALID_DRIVE: errno.ENODEV,
70 winerror.ERROR_INVALID_DRIVE: errno.ENODEV,
71 winerror.ERROR_INVALID_EXE_SIGNATURE: errno.ENOEXEC,
71 winerror.ERROR_INVALID_EXE_SIGNATURE: errno.ENOEXEC,
72 winerror.ERROR_INVALID_FLAGS: errno.EINVAL,
72 winerror.ERROR_INVALID_FLAGS: errno.EINVAL,
73 winerror.ERROR_INVALID_FUNCTION: errno.ENOSYS,
73 winerror.ERROR_INVALID_FUNCTION: errno.ENOSYS,
74 winerror.ERROR_INVALID_HANDLE: errno.EBADF,
74 winerror.ERROR_INVALID_HANDLE: errno.EBADF,
75 winerror.ERROR_INVALID_LOGON_HOURS: errno.EACCES,
75 winerror.ERROR_INVALID_LOGON_HOURS: errno.EACCES,
76 winerror.ERROR_INVALID_NAME: errno.EINVAL,
76 winerror.ERROR_INVALID_NAME: errno.EINVAL,
77 winerror.ERROR_INVALID_OWNER: errno.EINVAL,
77 winerror.ERROR_INVALID_OWNER: errno.EINVAL,
78 winerror.ERROR_INVALID_PARAMETER: errno.EINVAL,
78 winerror.ERROR_INVALID_PARAMETER: errno.EINVAL,
79 winerror.ERROR_INVALID_PASSWORD: errno.EPERM,
79 winerror.ERROR_INVALID_PASSWORD: errno.EPERM,
80 winerror.ERROR_INVALID_PRIMARY_GROUP: errno.EINVAL,
80 winerror.ERROR_INVALID_PRIMARY_GROUP: errno.EINVAL,
81 winerror.ERROR_INVALID_SIGNAL_NUMBER: errno.EINVAL,
81 winerror.ERROR_INVALID_SIGNAL_NUMBER: errno.EINVAL,
82 winerror.ERROR_INVALID_TARGET_HANDLE: errno.EIO,
82 winerror.ERROR_INVALID_TARGET_HANDLE: errno.EIO,
83 winerror.ERROR_INVALID_WORKSTATION: errno.EACCES,
83 winerror.ERROR_INVALID_WORKSTATION: errno.EACCES,
84 winerror.ERROR_IO_DEVICE: errno.EIO,
84 winerror.ERROR_IO_DEVICE: errno.EIO,
85 winerror.ERROR_IO_INCOMPLETE: errno.EINTR,
85 winerror.ERROR_IO_INCOMPLETE: errno.EINTR,
86 winerror.ERROR_LOCKED: errno.EBUSY,
86 winerror.ERROR_LOCKED: errno.EBUSY,
87 winerror.ERROR_LOCK_VIOLATION: errno.EACCES,
87 winerror.ERROR_LOCK_VIOLATION: errno.EACCES,
88 winerror.ERROR_LOGON_FAILURE: errno.EACCES,
88 winerror.ERROR_LOGON_FAILURE: errno.EACCES,
89 winerror.ERROR_MAPPED_ALIGNMENT: errno.EINVAL,
89 winerror.ERROR_MAPPED_ALIGNMENT: errno.EINVAL,
90 winerror.ERROR_META_EXPANSION_TOO_LONG: errno.E2BIG,
90 winerror.ERROR_META_EXPANSION_TOO_LONG: errno.E2BIG,
91 winerror.ERROR_MORE_DATA: errno.EPIPE,
91 winerror.ERROR_MORE_DATA: errno.EPIPE,
92 winerror.ERROR_NEGATIVE_SEEK: errno.ESPIPE,
92 winerror.ERROR_NEGATIVE_SEEK: errno.ESPIPE,
93 winerror.ERROR_NOACCESS: errno.EFAULT,
93 winerror.ERROR_NOACCESS: errno.EFAULT,
94 winerror.ERROR_NONE_MAPPED: errno.EINVAL,
94 winerror.ERROR_NONE_MAPPED: errno.EINVAL,
95 winerror.ERROR_NOT_ENOUGH_MEMORY: errno.ENOMEM,
95 winerror.ERROR_NOT_ENOUGH_MEMORY: errno.ENOMEM,
96 winerror.ERROR_NOT_READY: errno.EAGAIN,
96 winerror.ERROR_NOT_READY: errno.EAGAIN,
97 winerror.ERROR_NOT_SAME_DEVICE: errno.EXDEV,
97 winerror.ERROR_NOT_SAME_DEVICE: errno.EXDEV,
98 winerror.ERROR_NO_DATA: errno.EPIPE,
98 winerror.ERROR_NO_DATA: errno.EPIPE,
99 winerror.ERROR_NO_MORE_SEARCH_HANDLES: errno.EIO,
99 winerror.ERROR_NO_MORE_SEARCH_HANDLES: errno.EIO,
100 winerror.ERROR_NO_PROC_SLOTS: errno.EAGAIN,
100 winerror.ERROR_NO_PROC_SLOTS: errno.EAGAIN,
101 winerror.ERROR_NO_SUCH_PRIVILEGE: errno.EACCES,
101 winerror.ERROR_NO_SUCH_PRIVILEGE: errno.EACCES,
102 winerror.ERROR_OPEN_FAILED: errno.EIO,
102 winerror.ERROR_OPEN_FAILED: errno.EIO,
103 winerror.ERROR_OPEN_FILES: errno.EBUSY,
103 winerror.ERROR_OPEN_FILES: errno.EBUSY,
104 winerror.ERROR_OPERATION_ABORTED: errno.EINTR,
104 winerror.ERROR_OPERATION_ABORTED: errno.EINTR,
105 winerror.ERROR_OUTOFMEMORY: errno.ENOMEM,
105 winerror.ERROR_OUTOFMEMORY: errno.ENOMEM,
106 winerror.ERROR_PASSWORD_EXPIRED: errno.EACCES,
106 winerror.ERROR_PASSWORD_EXPIRED: errno.EACCES,
107 winerror.ERROR_PATH_BUSY: errno.EBUSY,
107 winerror.ERROR_PATH_BUSY: errno.EBUSY,
108 winerror.ERROR_PATH_NOT_FOUND: errno.ENOENT,
108 winerror.ERROR_PATH_NOT_FOUND: errno.ENOENT,
109 winerror.ERROR_PIPE_BUSY: errno.EBUSY,
109 winerror.ERROR_PIPE_BUSY: errno.EBUSY,
110 winerror.ERROR_PIPE_CONNECTED: errno.EPIPE,
110 winerror.ERROR_PIPE_CONNECTED: errno.EPIPE,
111 winerror.ERROR_PIPE_LISTENING: errno.EPIPE,
111 winerror.ERROR_PIPE_LISTENING: errno.EPIPE,
112 winerror.ERROR_PIPE_NOT_CONNECTED: errno.EPIPE,
112 winerror.ERROR_PIPE_NOT_CONNECTED: errno.EPIPE,
113 winerror.ERROR_PRIVILEGE_NOT_HELD: errno.EACCES,
113 winerror.ERROR_PRIVILEGE_NOT_HELD: errno.EACCES,
114 winerror.ERROR_READ_FAULT: errno.EIO,
114 winerror.ERROR_READ_FAULT: errno.EIO,
115 winerror.ERROR_SEEK: errno.EIO,
115 winerror.ERROR_SEEK: errno.EIO,
116 winerror.ERROR_SEEK_ON_DEVICE: errno.ESPIPE,
116 winerror.ERROR_SEEK_ON_DEVICE: errno.ESPIPE,
117 winerror.ERROR_SHARING_BUFFER_EXCEEDED: errno.ENFILE,
117 winerror.ERROR_SHARING_BUFFER_EXCEEDED: errno.ENFILE,
118 winerror.ERROR_SHARING_VIOLATION: errno.EACCES,
118 winerror.ERROR_SHARING_VIOLATION: errno.EACCES,
119 winerror.ERROR_STACK_OVERFLOW: errno.ENOMEM,
119 winerror.ERROR_STACK_OVERFLOW: errno.ENOMEM,
120 winerror.ERROR_SWAPERROR: errno.ENOENT,
120 winerror.ERROR_SWAPERROR: errno.ENOENT,
121 winerror.ERROR_TOO_MANY_MODULES: errno.EMFILE,
121 winerror.ERROR_TOO_MANY_MODULES: errno.EMFILE,
122 winerror.ERROR_TOO_MANY_OPEN_FILES: errno.EMFILE,
122 winerror.ERROR_TOO_MANY_OPEN_FILES: errno.EMFILE,
123 winerror.ERROR_UNRECOGNIZED_MEDIA: errno.ENXIO,
123 winerror.ERROR_UNRECOGNIZED_MEDIA: errno.ENXIO,
124 winerror.ERROR_UNRECOGNIZED_VOLUME: errno.ENODEV,
124 winerror.ERROR_UNRECOGNIZED_VOLUME: errno.ENODEV,
125 winerror.ERROR_WAIT_NO_CHILDREN: errno.ECHILD,
125 winerror.ERROR_WAIT_NO_CHILDREN: errno.ECHILD,
126 winerror.ERROR_WRITE_FAULT: errno.EIO,
126 winerror.ERROR_WRITE_FAULT: errno.EIO,
127 winerror.ERROR_WRITE_PROTECT: errno.EROFS,
127 winerror.ERROR_WRITE_PROTECT: errno.EROFS,
128 }
128 }
129
129
130 def __init__(self, err):
130 def __init__(self, err):
131 self.win_errno, self.win_function, self.win_strerror = err
131 self.win_errno, self.win_function, self.win_strerror = err
132 if self.win_strerror.endswith('.'):
132 if self.win_strerror.endswith('.'):
133 self.win_strerror = self.win_strerror[:-1]
133 self.win_strerror = self.win_strerror[:-1]
134
134
135 class WinIOError(WinError, IOError):
135 class WinIOError(WinError, IOError):
136 def __init__(self, err, filename=None):
136 def __init__(self, err, filename=None):
137 WinError.__init__(self, err)
137 WinError.__init__(self, err)
138 IOError.__init__(self, self.winerror_map.get(self.win_errno, 0),
138 IOError.__init__(self, self.winerror_map.get(self.win_errno, 0),
139 self.win_strerror)
139 self.win_strerror)
140 self.filename = filename
140 self.filename = filename
141
141
142 class WinOSError(WinError, OSError):
142 class WinOSError(WinError, OSError):
143 def __init__(self, err):
143 def __init__(self, err):
144 WinError.__init__(self, err)
144 WinError.__init__(self, err)
145 OSError.__init__(self, self.winerror_map.get(self.win_errno, 0),
145 OSError.__init__(self, self.winerror_map.get(self.win_errno, 0),
146 self.win_strerror)
146 self.win_strerror)
147
147
148 def os_link(src, dst):
148 def os_link(src, dst):
149 # NB will only succeed on NTFS
149 # NB will only succeed on NTFS
150 try:
150 try:
151 win32file.CreateHardLink(dst, src)
151 win32file.CreateHardLink(dst, src)
152 except pywintypes.error, details:
152 except pywintypes.error, details:
153 raise WinOSError(details)
153 raise WinOSError(details)
154
154
155 def nlinks(pathname):
155 def nlinks(pathname):
156 """Return number of hardlinks for the given file."""
156 """Return number of hardlinks for the given file."""
157 try:
157 try:
158 fh = win32file.CreateFile(pathname,
158 fh = win32file.CreateFile(pathname,
159 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
159 win32file.GENERIC_READ, win32file.FILE_SHARE_READ,
160 None, win32file.OPEN_EXISTING, 0, None)
160 None, win32file.OPEN_EXISTING, 0, None)
161 res = win32file.GetFileInformationByHandle(fh)
161 res = win32file.GetFileInformationByHandle(fh)
162 fh.Close()
162 fh.Close()
163 return res[7]
163 return res[7]
164 except pywintypes.error:
164 except pywintypes.error:
165 return os.lstat(pathname).st_nlink
165 return os.lstat(pathname).st_nlink
166
166
167 def testpid(pid):
167 def testpid(pid):
168 '''return True if pid is still running or unable to
168 '''return True if pid is still running or unable to
169 determine, False otherwise'''
169 determine, False otherwise'''
170 try:
170 try:
171 handle = win32api.OpenProcess(
171 handle = win32api.OpenProcess(
172 win32con.PROCESS_QUERY_INFORMATION, False, pid)
172 win32con.PROCESS_QUERY_INFORMATION, False, pid)
173 if handle:
173 if handle:
174 status = win32process.GetExitCodeProcess(handle)
174 status = win32process.GetExitCodeProcess(handle)
175 return status == win32con.STILL_ACTIVE
175 return status == win32con.STILL_ACTIVE
176 except pywintypes.error, details:
176 except pywintypes.error, details:
177 return details[0] != winerror.ERROR_INVALID_PARAMETER
177 return details[0] != winerror.ERROR_INVALID_PARAMETER
178 return True
178 return True
179
179
180 def system_rcpath_win32():
180 def system_rcpath_win32():
181 '''return default os-specific hgrc search path'''
181 '''return default os-specific hgrc search path'''
182 proc = win32api.GetCurrentProcess()
182 proc = win32api.GetCurrentProcess()
183 try:
183 try:
184 # This will fail on windows < NT
184 # This will fail on windows < NT
185 filename = win32process.GetModuleFileNameEx(proc, 0)
185 filename = win32process.GetModuleFileNameEx(proc, 0)
186 except:
186 except:
187 filename = win32api.GetModuleFileName(0)
187 filename = win32api.GetModuleFileName(0)
188 return [os.path.join(os.path.dirname(filename), 'mercurial.ini')]
188 return [os.path.join(os.path.dirname(filename), 'mercurial.ini')]
189
189
190 def user_rcpath_win32():
190 def user_rcpath_win32():
191 '''return os-specific hgrc search path to the user dir'''
191 '''return os-specific hgrc search path to the user dir'''
192 userdir = os.path.expanduser('~')
192 userdir = os.path.expanduser('~')
193 if userdir == '~':
193 if userdir == '~':
194 # We are on win < nt: fetch the APPDATA directory location and use
194 # We are on win < nt: fetch the APPDATA directory location and use
195 # the parent directory as the user home dir.
195 # the parent directory as the user home dir.
196 appdir = shell.SHGetPathFromIDList(
196 appdir = shell.SHGetPathFromIDList(
197 shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA))
197 shell.SHGetSpecialFolderLocation(0, shellcon.CSIDL_APPDATA))
198 userdir = os.path.dirname(appdir)
198 userdir = os.path.dirname(appdir)
199 return os.path.join(userdir, 'mercurial.ini')
199 return os.path.join(userdir, 'mercurial.ini')
200
200
201 class posixfile_nt(object):
201 class posixfile_nt(object):
202 '''file object with posix-like semantics. on windows, normal
202 '''file object with posix-like semantics. on windows, normal
203 files can not be deleted or renamed if they are open. must open
203 files can not be deleted or renamed if they are open. must open
204 with win32file.FILE_SHARE_DELETE. this flag does not exist on
204 with win32file.FILE_SHARE_DELETE. this flag does not exist on
205 windows < nt, so do not use this class there.'''
205 windows < nt, so do not use this class there.'''
206
206
207 # tried to use win32file._open_osfhandle to pass fd to os.fdopen,
207 # tried to use win32file._open_osfhandle to pass fd to os.fdopen,
208 # but does not work at all. wrap win32 file api instead.
208 # but does not work at all. wrap win32 file api instead.
209
209
210 def __init__(self, name, mode='rb'):
210 def __init__(self, name, mode='rb'):
211 access = 0
211 access = 0
212 if 'r' in mode or '+' in mode:
212 if 'r' in mode or '+' in mode:
213 access |= win32file.GENERIC_READ
213 access |= win32file.GENERIC_READ
214 if 'w' in mode or 'a' in mode:
214 if 'w' in mode or 'a' in mode:
215 access |= win32file.GENERIC_WRITE
215 access |= win32file.GENERIC_WRITE
216 if 'r' in mode:
216 if 'r' in mode:
217 creation = win32file.OPEN_EXISTING
217 creation = win32file.OPEN_EXISTING
218 elif 'a' in mode:
218 elif 'a' in mode:
219 creation = win32file.OPEN_ALWAYS
219 creation = win32file.OPEN_ALWAYS
220 else:
220 else:
221 creation = win32file.CREATE_ALWAYS
221 creation = win32file.CREATE_ALWAYS
222 try:
222 try:
223 self.handle = win32file.CreateFile(name,
223 self.handle = win32file.CreateFile(name,
224 access,
224 access,
225 win32file.FILE_SHARE_READ |
225 win32file.FILE_SHARE_READ |
226 win32file.FILE_SHARE_WRITE |
226 win32file.FILE_SHARE_WRITE |
227 win32file.FILE_SHARE_DELETE,
227 win32file.FILE_SHARE_DELETE,
228 None,
228 None,
229 creation,
229 creation,
230 win32file.FILE_ATTRIBUTE_NORMAL,
230 win32file.FILE_ATTRIBUTE_NORMAL,
231 0)
231 0)
232 except pywintypes.error, err:
232 except pywintypes.error, err:
233 raise WinIOError(err, name)
233 raise WinIOError(err, name)
234 self.closed = False
234 self.closed = False
235 self.name = name
235 self.name = name
236 self.mode = mode
236 self.mode = mode
237
237
238 def __iter__(self):
238 def __iter__(self):
239 for line in self.read().splitlines(True):
239 for line in self.read().splitlines(True):
240 yield line
240 yield line
241
241
242 def read(self, count=-1):
242 def read(self, count=-1):
243 try:
243 try:
244 cs = cStringIO.StringIO()
244 cs = cStringIO.StringIO()
245 while count:
245 while count:
246 wincount = int(count)
246 wincount = int(count)
247 if wincount == -1:
247 if wincount == -1:
248 wincount = 1048576
248 wincount = 1048576
249 val, data = win32file.ReadFile(self.handle, wincount)
249 val, data = win32file.ReadFile(self.handle, wincount)
250 if not data: break
250 if not data: break
251 cs.write(data)
251 cs.write(data)
252 if count != -1:
252 if count != -1:
253 count -= len(data)
253 count -= len(data)
254 return cs.getvalue()
254 return cs.getvalue()
255 except pywintypes.error, err:
255 except pywintypes.error, err:
256 raise WinIOError(err)
256 raise WinIOError(err)
257
257
258 def write(self, data):
258 def write(self, data):
259 try:
259 try:
260 if 'a' in self.mode:
260 if 'a' in self.mode:
261 win32file.SetFilePointer(self.handle, 0, win32file.FILE_END)
261 win32file.SetFilePointer(self.handle, 0, win32file.FILE_END)
262 nwrit = 0
262 nwrit = 0
263 while nwrit < len(data):
263 while nwrit < len(data):
264 val, nwrit = win32file.WriteFile(self.handle, data)
264 val, nwrit = win32file.WriteFile(self.handle, data)
265 data = data[nwrit:]
265 data = data[nwrit:]
266 except pywintypes.error, err:
266 except pywintypes.error, err:
267 raise WinIOError(err)
267 raise WinIOError(err)
268
268
269 def seek(self, pos, whence=0):
269 def seek(self, pos, whence=0):
270 try:
270 try:
271 win32file.SetFilePointer(self.handle, int(pos), whence)
271 win32file.SetFilePointer(self.handle, int(pos), whence)
272 except pywintypes.error, err:
272 except pywintypes.error, err:
273 raise WinIOError(err)
273 raise WinIOError(err)
274
274
275 def tell(self):
275 def tell(self):
276 try:
276 try:
277 return win32file.SetFilePointer(self.handle, 0,
277 return win32file.SetFilePointer(self.handle, 0,
278 win32file.FILE_CURRENT)
278 win32file.FILE_CURRENT)
279 except pywintypes.error, err:
279 except pywintypes.error, err:
280 raise WinIOError(err)
280 raise WinIOError(err)
281
281
282 def close(self):
282 def close(self):
283 if not self.closed:
283 if not self.closed:
284 self.handle = None
284 self.handle = None
285 self.closed = True
285 self.closed = True
286
286
287 def flush(self):
287 def flush(self):
288 try:
288 try:
289 win32file.FlushFileBuffers(self.handle)
289 win32file.FlushFileBuffers(self.handle)
290 except pywintypes.error, err:
290 except pywintypes.error, err:
291 raise WinIOError(err)
291 raise WinIOError(err)
292
292
293 def truncate(self, pos=0):
293 def truncate(self, pos=0):
294 try:
294 try:
295 win32file.SetFilePointer(self.handle, int(pos),
295 win32file.SetFilePointer(self.handle, int(pos),
296 win32file.FILE_BEGIN)
296 win32file.FILE_BEGIN)
297 win32file.SetEndOfFile(self.handle)
297 win32file.SetEndOfFile(self.handle)
298 except pywintypes.error, err:
298 except pywintypes.error, err:
299 raise WinIOError(err)
299 raise WinIOError(err)
300
300
301 getuser_fallback = win32api.GetUserName
301 getuser_fallback = win32api.GetUserName
302
302
303 def set_signal_handler():
303 def set_signal_handler_win32():
304 """Register a termination handler for console events including
304 """Register a termination handler for console events including
305 CTRL+C. python signal handlers do not work well with socket
305 CTRL+C. python signal handlers do not work well with socket
306 operations.
306 operations.
307 """
307 """
308 def handler(event):
308 def handler(event):
309 win32process.ExitProcess(1)
309 win32process.ExitProcess(1)
310 win32api.SetConsoleCtrlHandler(handler)
310 win32api.SetConsoleCtrlHandler(handler)
@@ -1,59 +1,65
1 #!/bin/sh
1 #!/bin/sh
2
2
3 mkdir test
3 mkdir test
4 cd test
4 cd test
5 hg init
5 hg init
6 echo foo>foo
6 echo foo>foo
7 hg commit -Am 1 -d '1 0'
7 hg commit -Am 1 -d '1 0'
8 echo bar>bar
8 echo bar>bar
9 hg commit -Am 2 -d '2 0'
9 hg commit -Am 2 -d '2 0'
10 mkdir baz
10 mkdir baz
11 echo bletch>baz/bletch
11 echo bletch>baz/bletch
12 hg commit -Am 3 -d '1000000000 0'
12 hg commit -Am 3 -d '1000000000 0'
13 echo "[web]" >> .hg/hgrc
13 echo "[web]" >> .hg/hgrc
14 echo "name = test-archive" >> .hg/hgrc
14 echo "name = test-archive" >> .hg/hgrc
15 echo "allow_archive = gz bz2, zip" >> .hg/hgrc
15 echo "allow_archive = gz bz2, zip" >> .hg/hgrc
16 hg serve -p 20059 -d --pid-file=hg.pid
16 hg serve -p 20059 -d --pid-file=hg.pid
17 cat hg.pid >> $DAEMON_PIDS
17 cat hg.pid >> $DAEMON_PIDS
18
18
19 TIP=`hg id -v | cut -f1 -d' '`
19 TIP=`hg id -v | cut -f1 -d' '`
20 QTIP=`hg id -q`
20 QTIP=`hg id -q`
21 cat > getarchive.py <<EOF
21 cat > getarchive.py <<EOF
22 import sys, urllib2
22 import sys, urllib2
23 node, archive = sys.argv[1:]
23 node, archive = sys.argv[1:]
24 f = urllib2.urlopen('http://127.0.0.1:20059/?cmd=archive;node=%s;type=%s'
24 f = urllib2.urlopen('http://127.0.0.1:20059/?cmd=archive;node=%s;type=%s'
25 % (node, archive))
25 % (node, archive))
26 sys.stdout.write(f.read())
26 sys.stdout.write(f.read())
27 EOF
27 EOF
28 http_proxy= python getarchive.py "$TIP" gz | gunzip | tar tf - | sed "s/$QTIP/TIP/"
28 http_proxy= python getarchive.py "$TIP" gz | gunzip | tar tf - | sed "s/$QTIP/TIP/"
29 http_proxy= python getarchive.py "$TIP" bz2 | bunzip2 | tar tf - | sed "s/$QTIP/TIP/"
29 http_proxy= python getarchive.py "$TIP" bz2 | bunzip2 | tar tf - | sed "s/$QTIP/TIP/"
30 http_proxy= python getarchive.py "$TIP" zip > archive.zip
30 http_proxy= python getarchive.py "$TIP" zip > archive.zip
31 unzip -t archive.zip | sed "s/$QTIP/TIP/"
31 unzip -t archive.zip | sed "s/$QTIP/TIP/"
32
32
33 hg archive -t tar test.tar
33 hg archive -t tar test.tar
34 tar tf test.tar
34 tar tf test.tar
35
35
36 hg archive -t tbz2 -X baz test.tar.bz2
36 hg archive -t tbz2 -X baz test.tar.bz2
37 bunzip2 -dc test.tar.bz2 | tar tf -
37 bunzip2 -dc test.tar.bz2 | tar tf -
38
38
39 hg archive -t tgz -p %b-%h test-%h.tar.gz
39 hg archive -t tgz -p %b-%h test-%h.tar.gz
40 gzip -dc test-$QTIP.tar.gz | tar tf - | sed "s/$QTIP/TIP/"
40 gzip -dc test-$QTIP.tar.gz | tar tf - | sed "s/$QTIP/TIP/"
41
41
42 cat > md5check.py <<EOF
42 cat > md5comp.py <<EOF
43 import md5, sys
43 import md5, sys
44 print md5.md5(file(sys.argv[1], 'rb').read()).hexdigest()
44 f1, f2 = sys.argv[1:3]
45 h1 = md5.md5(file(f1, 'rb').read()).hexdigest()
46 h2 = md5.md5(file(f2, 'rb').read()).hexdigest()
47 print h1 == h2 or "md5 differ: " + repr((h1, h2))
45 EOF
48 EOF
46
49
50 # archive name is stored in the archive, so create similar
51 # archives and rename them afterwards.
47 hg archive -t tgz tip.tar.gz
52 hg archive -t tgz tip.tar.gz
48 python md5check.py tip.tar.gz
53 mv tip.tar.gz tip1.tar.gz
49 sleep 1
54 sleep 1
50 hg archive -t tgz tip.tar.gz
55 hg archive -t tgz tip.tar.gz
51 python md5check.py tip.tar.gz
56 mv tip.tar.gz tip2.tar.gz
57 python md5comp.py tip1.tar.gz tip2.tar.gz
52
58
53 hg archive -t zip -p /illegal test.zip
59 hg archive -t zip -p /illegal test.zip
54 hg archive -t zip -p very/../bad test.zip
60 hg archive -t zip -p very/../bad test.zip
55
61
56 hg archive -t zip -r 2 test.zip
62 hg archive -t zip -r 2 test.zip
57 unzip -t test.zip
63 unzip -t test.zip
58
64
59 hg archive -t tar - | tar tf - | sed "s/$QTIP/TIP/"
65 hg archive -t tar - | tar tf - | sed "s/$QTIP/TIP/"
@@ -1,41 +1,40
1 adding foo
1 adding foo
2 adding bar
2 adding bar
3 adding baz/bletch
3 adding baz/bletch
4 test-archive-TIP/.hg_archival.txt
4 test-archive-TIP/.hg_archival.txt
5 test-archive-TIP/bar
5 test-archive-TIP/bar
6 test-archive-TIP/baz/bletch
6 test-archive-TIP/baz/bletch
7 test-archive-TIP/foo
7 test-archive-TIP/foo
8 test-archive-TIP/.hg_archival.txt
8 test-archive-TIP/.hg_archival.txt
9 test-archive-TIP/bar
9 test-archive-TIP/bar
10 test-archive-TIP/baz/bletch
10 test-archive-TIP/baz/bletch
11 test-archive-TIP/foo
11 test-archive-TIP/foo
12 Archive: archive.zip
12 Archive: archive.zip
13 testing: test-archive-TIP/.hg_archival.txt OK
13 testing: test-archive-TIP/.hg_archival.txt OK
14 testing: test-archive-TIP/bar OK
14 testing: test-archive-TIP/bar OK
15 testing: test-archive-TIP/baz/bletch OK
15 testing: test-archive-TIP/baz/bletch OK
16 testing: test-archive-TIP/foo OK
16 testing: test-archive-TIP/foo OK
17 No errors detected in compressed data of archive.zip.
17 No errors detected in compressed data of archive.zip.
18 test/.hg_archival.txt
18 test/.hg_archival.txt
19 test/bar
19 test/bar
20 test/baz/bletch
20 test/baz/bletch
21 test/foo
21 test/foo
22 test/.hg_archival.txt
22 test/.hg_archival.txt
23 test/bar
23 test/bar
24 test/foo
24 test/foo
25 test-TIP/.hg_archival.txt
25 test-TIP/.hg_archival.txt
26 test-TIP/bar
26 test-TIP/bar
27 test-TIP/baz/bletch
27 test-TIP/baz/bletch
28 test-TIP/foo
28 test-TIP/foo
29 76ce33f5d5cf8151558e2d9a396c7504
29 True
30 76ce33f5d5cf8151558e2d9a396c7504
31 abort: archive prefix contains illegal components
30 abort: archive prefix contains illegal components
32 Archive: test.zip
31 Archive: test.zip
33 testing: test/.hg_archival.txt OK
32 testing: test/.hg_archival.txt OK
34 testing: test/bar OK
33 testing: test/bar OK
35 testing: test/baz/bletch OK
34 testing: test/baz/bletch OK
36 testing: test/foo OK
35 testing: test/foo OK
37 No errors detected in compressed data of test.zip.
36 No errors detected in compressed data of test.zip.
38 test-TIP/.hg_archival.txt
37 test-TIP/.hg_archival.txt
39 test-TIP/bar
38 test-TIP/bar
40 test-TIP/baz/bletch
39 test-TIP/baz/bletch
41 test-TIP/foo
40 test-TIP/foo
@@ -1,47 +1,47
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2
2
3 from mercurial import demandimport
3 from mercurial import demandimport
4 demandimport.enable()
4 demandimport.enable()
5
5
6 import re
6 import re
7
7
8 rsub = re.sub
8 rsub = re.sub
9 def f(obj):
9 def f(obj):
10 l = repr(obj)
10 l = repr(obj)
11 l = rsub("0x[0-9a-f]+", "0x?", l)
11 l = rsub("0x[0-9a-fA-F]+", "0x?", l)
12 l = rsub("from '.*'", "from '?'", l)
12 l = rsub("from '.*'", "from '?'", l)
13 return l
13 return l
14
14
15 import os
15 import os
16
16
17 print "os =", f(os)
17 print "os =", f(os)
18 print "os.system =", f(os.system)
18 print "os.system =", f(os.system)
19 print "os =", f(os)
19 print "os =", f(os)
20
20
21 import mercurial.version
21 import mercurial.version
22
22
23 print "mercurial.version =", f(mercurial.version)
23 print "mercurial.version =", f(mercurial.version)
24 print "mercurial.version.get_version =", f(mercurial.version.get_version)
24 print "mercurial.version.get_version =", f(mercurial.version.get_version)
25 print "mercurial.version =", f(mercurial.version)
25 print "mercurial.version =", f(mercurial.version)
26 print "mercurial =", f(mercurial)
26 print "mercurial =", f(mercurial)
27
27
28 from mercurial import util
28 from mercurial import util
29
29
30 print "util =", f(util)
30 print "util =", f(util)
31 print "util.system =", f(util.system)
31 print "util.system =", f(util.system)
32 print "util =", f(util)
32 print "util =", f(util)
33 print "util.system =", f(util.system)
33 print "util.system =", f(util.system)
34
34
35 import re as fred
35 import re as fred
36 print "fred =", f(fred)
36 print "fred =", f(fred)
37
37
38 import sys as re
38 import sys as re
39 print "re =", f(re)
39 print "re =", f(re)
40
40
41 print "fred =", f(fred)
41 print "fred =", f(fred)
42 print "fred.sub =", f(fred.sub)
42 print "fred.sub =", f(fred.sub)
43 print "fred =", f(fred)
43 print "fred =", f(fred)
44
44
45 print "re =", f(re)
45 print "re =", f(re)
46 print "re.stdout =", f(re.stdout)
46 print "re.stdout =", f(re.stdout)
47 print "re =", f(re)
47 print "re =", f(re)
General Comments 0
You need to be logged in to leave comments. Login now