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