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