##// END OF EJS Templates
merge with crew-stable
Alexis S. L. Carvalho -
r4782:37e11c76 merge default
parent child Browse files
Show More
@@ -1,3118 +1,3124 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import demandimport; demandimport.enable()
8 import demandimport; demandimport.enable()
9 from node import *
9 from node import *
10 from i18n import _
10 from i18n import _
11 import bisect, os, re, sys, urllib, shlex, stat
11 import bisect, os, re, sys, urllib, shlex, stat
12 import ui, hg, util, revlog, bundlerepo, extensions
12 import ui, hg, util, revlog, bundlerepo, extensions
13 import difflib, patch, time, help, mdiff, tempfile
13 import difflib, patch, time, help, mdiff, tempfile
14 import errno, version, socket
14 import errno, version, socket
15 import archival, changegroup, cmdutil, hgweb.server, sshserver
15 import archival, changegroup, cmdutil, hgweb.server, sshserver
16
16
17 # Commands start here, listed alphabetically
17 # Commands start here, listed alphabetically
18
18
19 def add(ui, repo, *pats, **opts):
19 def add(ui, repo, *pats, **opts):
20 """add the specified files on the next commit
20 """add the specified files on the next commit
21
21
22 Schedule files to be version controlled and added to the repository.
22 Schedule files to be version controlled and added to the repository.
23
23
24 The files will be added to the repository at the next commit. To
24 The files will be added to the repository at the next commit. To
25 undo an add before that, see hg revert.
25 undo an add before that, see hg revert.
26
26
27 If no names are given, add all files in the repository.
27 If no names are given, add all files in the repository.
28 """
28 """
29
29
30 names = []
30 names = []
31 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
31 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
32 if exact:
32 if exact:
33 if ui.verbose:
33 if ui.verbose:
34 ui.status(_('adding %s\n') % rel)
34 ui.status(_('adding %s\n') % rel)
35 names.append(abs)
35 names.append(abs)
36 elif repo.dirstate.state(abs) == '?':
36 elif repo.dirstate.state(abs) == '?':
37 ui.status(_('adding %s\n') % rel)
37 ui.status(_('adding %s\n') % rel)
38 names.append(abs)
38 names.append(abs)
39 if not opts.get('dry_run'):
39 if not opts.get('dry_run'):
40 repo.add(names)
40 repo.add(names)
41
41
42 def addremove(ui, repo, *pats, **opts):
42 def addremove(ui, repo, *pats, **opts):
43 """add all new files, delete all missing files
43 """add all new files, delete all missing files
44
44
45 Add all new files and remove all missing files from the repository.
45 Add all new files and remove all missing files from the repository.
46
46
47 New files are ignored if they match any of the patterns in .hgignore. As
47 New files are ignored if they match any of the patterns in .hgignore. As
48 with add, these changes take effect at the next commit.
48 with add, these changes take effect at the next commit.
49
49
50 Use the -s option to detect renamed files. With a parameter > 0,
50 Use the -s option to detect renamed files. With a parameter > 0,
51 this compares every removed file with every added file and records
51 this compares every removed file with every added file and records
52 those similar enough as renames. This option takes a percentage
52 those similar enough as renames. This option takes a percentage
53 between 0 (disabled) and 100 (files must be identical) as its
53 between 0 (disabled) and 100 (files must be identical) as its
54 parameter. Detecting renamed files this way can be expensive.
54 parameter. Detecting renamed files this way can be expensive.
55 """
55 """
56 sim = float(opts.get('similarity') or 0)
56 sim = float(opts.get('similarity') or 0)
57 if sim < 0 or sim > 100:
57 if sim < 0 or sim > 100:
58 raise util.Abort(_('similarity must be between 0 and 100'))
58 raise util.Abort(_('similarity must be between 0 and 100'))
59 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
59 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
60
60
61 def annotate(ui, repo, *pats, **opts):
61 def annotate(ui, repo, *pats, **opts):
62 """show changeset information per file line
62 """show changeset information per file line
63
63
64 List changes in files, showing the revision id responsible for each line
64 List changes in files, showing the revision id responsible for each line
65
65
66 This command is useful to discover who did a change or when a change took
66 This command is useful to discover who did a change or when a change took
67 place.
67 place.
68
68
69 Without the -a option, annotate will avoid processing files it
69 Without the -a option, annotate will avoid processing files it
70 detects as binary. With -a, annotate will generate an annotation
70 detects as binary. With -a, annotate will generate an annotation
71 anyway, probably with undesirable results.
71 anyway, probably with undesirable results.
72 """
72 """
73 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
73 getdate = util.cachefunc(lambda x: util.datestr(x.date()))
74
74
75 if not pats:
75 if not pats:
76 raise util.Abort(_('at least one file name or pattern required'))
76 raise util.Abort(_('at least one file name or pattern required'))
77
77
78 opmap = [['user', lambda x: ui.shortuser(x.user())],
78 opmap = [['user', lambda x: ui.shortuser(x.user())],
79 ['number', lambda x: str(x.rev())],
79 ['number', lambda x: str(x.rev())],
80 ['changeset', lambda x: short(x.node())],
80 ['changeset', lambda x: short(x.node())],
81 ['date', getdate], ['follow', lambda x: x.path()]]
81 ['date', getdate], ['follow', lambda x: x.path()]]
82 if (not opts['user'] and not opts['changeset'] and not opts['date']
82 if (not opts['user'] and not opts['changeset'] and not opts['date']
83 and not opts['follow']):
83 and not opts['follow']):
84 opts['number'] = 1
84 opts['number'] = 1
85
85
86 ctx = repo.changectx(opts['rev'])
86 ctx = repo.changectx(opts['rev'])
87
87
88 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
88 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
89 node=ctx.node()):
89 node=ctx.node()):
90 fctx = ctx.filectx(abs)
90 fctx = ctx.filectx(abs)
91 if not opts['text'] and util.binary(fctx.data()):
91 if not opts['text'] and util.binary(fctx.data()):
92 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
92 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
93 continue
93 continue
94
94
95 lines = fctx.annotate(follow=opts.get('follow'))
95 lines = fctx.annotate(follow=opts.get('follow'))
96 pieces = []
96 pieces = []
97
97
98 for o, f in opmap:
98 for o, f in opmap:
99 if opts[o]:
99 if opts[o]:
100 l = [f(n) for n, dummy in lines]
100 l = [f(n) for n, dummy in lines]
101 if l:
101 if l:
102 m = max(map(len, l))
102 m = max(map(len, l))
103 pieces.append(["%*s" % (m, x) for x in l])
103 pieces.append(["%*s" % (m, x) for x in l])
104
104
105 if pieces:
105 if pieces:
106 for p, l in zip(zip(*pieces), lines):
106 for p, l in zip(zip(*pieces), lines):
107 ui.write("%s: %s" % (" ".join(p), l[1]))
107 ui.write("%s: %s" % (" ".join(p), l[1]))
108
108
109 def archive(ui, repo, dest, **opts):
109 def archive(ui, repo, dest, **opts):
110 '''create unversioned archive of a repository revision
110 '''create unversioned archive of a repository revision
111
111
112 By default, the revision used is the parent of the working
112 By default, the revision used is the parent of the working
113 directory; use "-r" to specify a different revision.
113 directory; use "-r" to specify a different revision.
114
114
115 To specify the type of archive to create, use "-t". Valid
115 To specify the type of archive to create, use "-t". Valid
116 types are:
116 types are:
117
117
118 "files" (default): a directory full of files
118 "files" (default): a directory full of files
119 "tar": tar archive, uncompressed
119 "tar": tar archive, uncompressed
120 "tbz2": tar archive, compressed using bzip2
120 "tbz2": tar archive, compressed using bzip2
121 "tgz": tar archive, compressed using gzip
121 "tgz": tar archive, compressed using gzip
122 "uzip": zip archive, uncompressed
122 "uzip": zip archive, uncompressed
123 "zip": zip archive, compressed using deflate
123 "zip": zip archive, compressed using deflate
124
124
125 The exact name of the destination archive or directory is given
125 The exact name of the destination archive or directory is given
126 using a format string; see "hg help export" for details.
126 using a format string; see "hg help export" for details.
127
127
128 Each member added to an archive file has a directory prefix
128 Each member added to an archive file has a directory prefix
129 prepended. Use "-p" to specify a format string for the prefix.
129 prepended. Use "-p" to specify a format string for the prefix.
130 The default is the basename of the archive, with suffixes removed.
130 The default is the basename of the archive, with suffixes removed.
131 '''
131 '''
132
132
133 node = repo.changectx(opts['rev']).node()
133 node = repo.changectx(opts['rev']).node()
134 dest = cmdutil.make_filename(repo, dest, node)
134 dest = cmdutil.make_filename(repo, dest, node)
135 if os.path.realpath(dest) == repo.root:
135 if os.path.realpath(dest) == repo.root:
136 raise util.Abort(_('repository root cannot be destination'))
136 raise util.Abort(_('repository root cannot be destination'))
137 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
137 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
138 kind = opts.get('type') or 'files'
138 kind = opts.get('type') or 'files'
139 prefix = opts['prefix']
139 prefix = opts['prefix']
140 if dest == '-':
140 if dest == '-':
141 if kind == 'files':
141 if kind == 'files':
142 raise util.Abort(_('cannot archive plain files to stdout'))
142 raise util.Abort(_('cannot archive plain files to stdout'))
143 dest = sys.stdout
143 dest = sys.stdout
144 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
144 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
145 prefix = cmdutil.make_filename(repo, prefix, node)
145 prefix = cmdutil.make_filename(repo, prefix, node)
146 archival.archive(repo, dest, node, kind, not opts['no_decode'],
146 archival.archive(repo, dest, node, kind, not opts['no_decode'],
147 matchfn, prefix)
147 matchfn, prefix)
148
148
149 def backout(ui, repo, node=None, rev=None, **opts):
149 def backout(ui, repo, node=None, rev=None, **opts):
150 '''reverse effect of earlier changeset
150 '''reverse effect of earlier changeset
151
151
152 Commit the backed out changes as a new changeset. The new
152 Commit the backed out changes as a new changeset. The new
153 changeset is a child of the backed out changeset.
153 changeset is a child of the backed out changeset.
154
154
155 If you back out a changeset other than the tip, a new head is
155 If you back out a changeset other than the tip, a new head is
156 created. This head is the parent of the working directory. If
156 created. This head is the parent of the working directory. If
157 you back out an old changeset, your working directory will appear
157 you back out an old changeset, your working directory will appear
158 old after the backout. You should merge the backout changeset
158 old after the backout. You should merge the backout changeset
159 with another head.
159 with another head.
160
160
161 The --merge option remembers the parent of the working directory
161 The --merge option remembers the parent of the working directory
162 before starting the backout, then merges the new head with that
162 before starting the backout, then merges the new head with that
163 changeset afterwards. This saves you from doing the merge by
163 changeset afterwards. This saves you from doing the merge by
164 hand. The result of this merge is not committed, as for a normal
164 hand. The result of this merge is not committed, as for a normal
165 merge.'''
165 merge.'''
166 if rev and node:
166 if rev and node:
167 raise util.Abort(_("please specify just one revision"))
167 raise util.Abort(_("please specify just one revision"))
168
168
169 if not rev:
169 if not rev:
170 rev = node
170 rev = node
171
171
172 if not rev:
172 if not rev:
173 raise util.Abort(_("please specify a revision to backout"))
173 raise util.Abort(_("please specify a revision to backout"))
174
174
175 cmdutil.bail_if_changed(repo)
175 cmdutil.bail_if_changed(repo)
176 op1, op2 = repo.dirstate.parents()
176 op1, op2 = repo.dirstate.parents()
177 if op2 != nullid:
177 if op2 != nullid:
178 raise util.Abort(_('outstanding uncommitted merge'))
178 raise util.Abort(_('outstanding uncommitted merge'))
179 node = repo.lookup(rev)
179 node = repo.lookup(rev)
180 p1, p2 = repo.changelog.parents(node)
180 p1, p2 = repo.changelog.parents(node)
181 if p1 == nullid:
181 if p1 == nullid:
182 raise util.Abort(_('cannot back out a change with no parents'))
182 raise util.Abort(_('cannot back out a change with no parents'))
183 if p2 != nullid:
183 if p2 != nullid:
184 if not opts['parent']:
184 if not opts['parent']:
185 raise util.Abort(_('cannot back out a merge changeset without '
185 raise util.Abort(_('cannot back out a merge changeset without '
186 '--parent'))
186 '--parent'))
187 p = repo.lookup(opts['parent'])
187 p = repo.lookup(opts['parent'])
188 if p not in (p1, p2):
188 if p not in (p1, p2):
189 raise util.Abort(_('%s is not a parent of %s') %
189 raise util.Abort(_('%s is not a parent of %s') %
190 (short(p), short(node)))
190 (short(p), short(node)))
191 parent = p
191 parent = p
192 else:
192 else:
193 if opts['parent']:
193 if opts['parent']:
194 raise util.Abort(_('cannot use --parent on non-merge changeset'))
194 raise util.Abort(_('cannot use --parent on non-merge changeset'))
195 parent = p1
195 parent = p1
196 hg.clean(repo, node, show_stats=False)
196 hg.clean(repo, node, show_stats=False)
197 revert_opts = opts.copy()
197 revert_opts = opts.copy()
198 revert_opts['date'] = None
198 revert_opts['date'] = None
199 revert_opts['all'] = True
199 revert_opts['all'] = True
200 revert_opts['rev'] = hex(parent)
200 revert_opts['rev'] = hex(parent)
201 revert(ui, repo, **revert_opts)
201 revert(ui, repo, **revert_opts)
202 commit_opts = opts.copy()
202 commit_opts = opts.copy()
203 commit_opts['addremove'] = False
203 commit_opts['addremove'] = False
204 if not commit_opts['message'] and not commit_opts['logfile']:
204 if not commit_opts['message'] and not commit_opts['logfile']:
205 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
205 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
206 commit_opts['force_editor'] = True
206 commit_opts['force_editor'] = True
207 commit(ui, repo, **commit_opts)
207 commit(ui, repo, **commit_opts)
208 def nice(node):
208 def nice(node):
209 return '%d:%s' % (repo.changelog.rev(node), short(node))
209 return '%d:%s' % (repo.changelog.rev(node), short(node))
210 ui.status(_('changeset %s backs out changeset %s\n') %
210 ui.status(_('changeset %s backs out changeset %s\n') %
211 (nice(repo.changelog.tip()), nice(node)))
211 (nice(repo.changelog.tip()), nice(node)))
212 if op1 != node:
212 if op1 != node:
213 if opts['merge']:
213 if opts['merge']:
214 ui.status(_('merging with changeset %s\n') % nice(op1))
214 ui.status(_('merging with changeset %s\n') % nice(op1))
215 hg.merge(repo, hex(op1))
215 hg.merge(repo, hex(op1))
216 else:
216 else:
217 ui.status(_('the backout changeset is a new head - '
217 ui.status(_('the backout changeset is a new head - '
218 'do not forget to merge\n'))
218 'do not forget to merge\n'))
219 ui.status(_('(use "backout --merge" '
219 ui.status(_('(use "backout --merge" '
220 'if you want to auto-merge)\n'))
220 'if you want to auto-merge)\n'))
221
221
222 def branch(ui, repo, label=None, **opts):
222 def branch(ui, repo, label=None, **opts):
223 """set or show the current branch name
223 """set or show the current branch name
224
224
225 With no argument, show the current branch name. With one argument,
225 With no argument, show the current branch name. With one argument,
226 set the working directory branch name (the branch does not exist in
226 set the working directory branch name (the branch does not exist in
227 the repository until the next commit).
227 the repository until the next commit).
228
228
229 Unless --force is specified, branch will not let you set a
229 Unless --force is specified, branch will not let you set a
230 branch name that shadows an existing branch.
230 branch name that shadows an existing branch.
231 """
231 """
232
232
233 if label:
233 if label:
234 if not opts.get('force') and label in repo.branchtags():
234 if not opts.get('force') and label in repo.branchtags():
235 if label not in [p.branch() for p in repo.workingctx().parents()]:
235 if label not in [p.branch() for p in repo.workingctx().parents()]:
236 raise util.Abort(_('a branch of the same name already exists'
236 raise util.Abort(_('a branch of the same name already exists'
237 ' (use --force to override)'))
237 ' (use --force to override)'))
238 repo.dirstate.setbranch(util.fromlocal(label))
238 repo.dirstate.setbranch(util.fromlocal(label))
239 ui.status(_('marked working directory as branch %s\n') % label)
239 ui.status(_('marked working directory as branch %s\n') % label)
240 else:
240 else:
241 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
241 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
242
242
243 def branches(ui, repo, active=False):
243 def branches(ui, repo, active=False):
244 """list repository named branches
244 """list repository named branches
245
245
246 List the repository's named branches, indicating which ones are
246 List the repository's named branches, indicating which ones are
247 inactive. If active is specified, only show active branches.
247 inactive. If active is specified, only show active branches.
248
248
249 A branch is considered active if it contains unmerged heads.
249 A branch is considered active if it contains unmerged heads.
250 """
250 """
251 b = repo.branchtags()
251 b = repo.branchtags()
252 heads = dict.fromkeys(repo.heads(), 1)
252 heads = dict.fromkeys(repo.heads(), 1)
253 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
253 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
254 l.sort()
254 l.sort()
255 l.reverse()
255 l.reverse()
256 for ishead, r, n, t in l:
256 for ishead, r, n, t in l:
257 if active and not ishead:
257 if active and not ishead:
258 # If we're only displaying active branches, abort the loop on
258 # If we're only displaying active branches, abort the loop on
259 # encountering the first inactive head
259 # encountering the first inactive head
260 break
260 break
261 else:
261 else:
262 hexfunc = ui.debugflag and hex or short
262 hexfunc = ui.debugflag and hex or short
263 if ui.quiet:
263 if ui.quiet:
264 ui.write("%s\n" % t)
264 ui.write("%s\n" % t)
265 else:
265 else:
266 spaces = " " * (30 - util.locallen(t))
266 spaces = " " * (30 - util.locallen(t))
267 # The code only gets here if inactive branches are being
267 # The code only gets here if inactive branches are being
268 # displayed or the branch is active.
268 # displayed or the branch is active.
269 isinactive = ((not ishead) and " (inactive)") or ''
269 isinactive = ((not ishead) and " (inactive)") or ''
270 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
270 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
271
271
272 def bundle(ui, repo, fname, dest=None, **opts):
272 def bundle(ui, repo, fname, dest=None, **opts):
273 """create a changegroup file
273 """create a changegroup file
274
274
275 Generate a compressed changegroup file collecting changesets not
275 Generate a compressed changegroup file collecting changesets not
276 found in the other repository.
276 found in the other repository.
277
277
278 If no destination repository is specified the destination is assumed
278 If no destination repository is specified the destination is assumed
279 to have all the nodes specified by one or more --base parameters.
279 to have all the nodes specified by one or more --base parameters.
280
280
281 The bundle file can then be transferred using conventional means and
281 The bundle file can then be transferred using conventional means and
282 applied to another repository with the unbundle or pull command.
282 applied to another repository with the unbundle or pull command.
283 This is useful when direct push and pull are not available or when
283 This is useful when direct push and pull are not available or when
284 exporting an entire repository is undesirable.
284 exporting an entire repository is undesirable.
285
285
286 Applying bundles preserves all changeset contents including
286 Applying bundles preserves all changeset contents including
287 permissions, copy/rename information, and revision history.
287 permissions, copy/rename information, and revision history.
288 """
288 """
289 revs = opts.get('rev') or None
289 revs = opts.get('rev') or None
290 if revs:
290 if revs:
291 revs = [repo.lookup(rev) for rev in revs]
291 revs = [repo.lookup(rev) for rev in revs]
292 base = opts.get('base')
292 base = opts.get('base')
293 if base:
293 if base:
294 if dest:
294 if dest:
295 raise util.Abort(_("--base is incompatible with specifiying "
295 raise util.Abort(_("--base is incompatible with specifiying "
296 "a destination"))
296 "a destination"))
297 base = [repo.lookup(rev) for rev in base]
297 base = [repo.lookup(rev) for rev in base]
298 # create the right base
298 # create the right base
299 # XXX: nodesbetween / changegroup* should be "fixed" instead
299 # XXX: nodesbetween / changegroup* should be "fixed" instead
300 o = []
300 o = []
301 has = {nullid: None}
301 has = {nullid: None}
302 for n in base:
302 for n in base:
303 has.update(repo.changelog.reachable(n))
303 has.update(repo.changelog.reachable(n))
304 if revs:
304 if revs:
305 visit = list(revs)
305 visit = list(revs)
306 else:
306 else:
307 visit = repo.changelog.heads()
307 visit = repo.changelog.heads()
308 seen = {}
308 seen = {}
309 while visit:
309 while visit:
310 n = visit.pop(0)
310 n = visit.pop(0)
311 parents = [p for p in repo.changelog.parents(n) if p not in has]
311 parents = [p for p in repo.changelog.parents(n) if p not in has]
312 if len(parents) == 0:
312 if len(parents) == 0:
313 o.insert(0, n)
313 o.insert(0, n)
314 else:
314 else:
315 for p in parents:
315 for p in parents:
316 if p not in seen:
316 if p not in seen:
317 seen[p] = 1
317 seen[p] = 1
318 visit.append(p)
318 visit.append(p)
319 else:
319 else:
320 cmdutil.setremoteconfig(ui, opts)
320 cmdutil.setremoteconfig(ui, opts)
321 dest, revs = cmdutil.parseurl(
321 dest, revs = cmdutil.parseurl(
322 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
322 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
323 other = hg.repository(ui, dest)
323 other = hg.repository(ui, dest)
324 o = repo.findoutgoing(other, force=opts['force'])
324 o = repo.findoutgoing(other, force=opts['force'])
325
325
326 if revs:
326 if revs:
327 cg = repo.changegroupsubset(o, revs, 'bundle')
327 cg = repo.changegroupsubset(o, revs, 'bundle')
328 else:
328 else:
329 cg = repo.changegroup(o, 'bundle')
329 cg = repo.changegroup(o, 'bundle')
330 changegroup.writebundle(cg, fname, "HG10BZ")
330 changegroup.writebundle(cg, fname, "HG10BZ")
331
331
332 def cat(ui, repo, file1, *pats, **opts):
332 def cat(ui, repo, file1, *pats, **opts):
333 """output the current or given revision of files
333 """output the current or given revision of files
334
334
335 Print the specified files as they were at the given revision.
335 Print the specified files as they were at the given revision.
336 If no revision is given, the parent of the working directory is used,
336 If no revision is given, the parent of the working directory is used,
337 or tip if no revision is checked out.
337 or tip if no revision is checked out.
338
338
339 Output may be to a file, in which case the name of the file is
339 Output may be to a file, in which case the name of the file is
340 given using a format string. The formatting rules are the same as
340 given using a format string. The formatting rules are the same as
341 for the export command, with the following additions:
341 for the export command, with the following additions:
342
342
343 %s basename of file being printed
343 %s basename of file being printed
344 %d dirname of file being printed, or '.' if in repo root
344 %d dirname of file being printed, or '.' if in repo root
345 %p root-relative path name of file being printed
345 %p root-relative path name of file being printed
346 """
346 """
347 ctx = repo.changectx(opts['rev'])
347 ctx = repo.changectx(opts['rev'])
348 err = 1
348 err = 1
349 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
349 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
350 ctx.node()):
350 ctx.node()):
351 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
351 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
352 fp.write(ctx.filectx(abs).data())
352 fp.write(ctx.filectx(abs).data())
353 err = 0
353 err = 0
354 return err
354 return err
355
355
356 def clone(ui, source, dest=None, **opts):
356 def clone(ui, source, dest=None, **opts):
357 """make a copy of an existing repository
357 """make a copy of an existing repository
358
358
359 Create a copy of an existing repository in a new directory.
359 Create a copy of an existing repository in a new directory.
360
360
361 If no destination directory name is specified, it defaults to the
361 If no destination directory name is specified, it defaults to the
362 basename of the source.
362 basename of the source.
363
363
364 The location of the source is added to the new repository's
364 The location of the source is added to the new repository's
365 .hg/hgrc file, as the default to be used for future pulls.
365 .hg/hgrc file, as the default to be used for future pulls.
366
366
367 For efficiency, hardlinks are used for cloning whenever the source
367 For efficiency, hardlinks are used for cloning whenever the source
368 and destination are on the same filesystem (note this applies only
368 and destination are on the same filesystem (note this applies only
369 to the repository data, not to the checked out files). Some
369 to the repository data, not to the checked out files). Some
370 filesystems, such as AFS, implement hardlinking incorrectly, but
370 filesystems, such as AFS, implement hardlinking incorrectly, but
371 do not report errors. In these cases, use the --pull option to
371 do not report errors. In these cases, use the --pull option to
372 avoid hardlinking.
372 avoid hardlinking.
373
373
374 You can safely clone repositories and checked out files using full
374 You can safely clone repositories and checked out files using full
375 hardlinks with
375 hardlinks with
376
376
377 $ cp -al REPO REPOCLONE
377 $ cp -al REPO REPOCLONE
378
378
379 which is the fastest way to clone. However, the operation is not
379 which is the fastest way to clone. However, the operation is not
380 atomic (making sure REPO is not modified during the operation is
380 atomic (making sure REPO is not modified during the operation is
381 up to you) and you have to make sure your editor breaks hardlinks
381 up to you) and you have to make sure your editor breaks hardlinks
382 (Emacs and most Linux Kernel tools do so).
382 (Emacs and most Linux Kernel tools do so).
383
383
384 If you use the -r option to clone up to a specific revision, no
384 If you use the -r option to clone up to a specific revision, no
385 subsequent revisions will be present in the cloned repository.
385 subsequent revisions will be present in the cloned repository.
386 This option implies --pull, even on local repositories.
386 This option implies --pull, even on local repositories.
387
387
388 See pull for valid source format details.
388 See pull for valid source format details.
389
389
390 It is possible to specify an ssh:// URL as the destination, but no
390 It is possible to specify an ssh:// URL as the destination, but no
391 .hg/hgrc and working directory will be created on the remote side.
391 .hg/hgrc and working directory will be created on the remote side.
392 Look at the help text for the pull command for important details
392 Look at the help text for the pull command for important details
393 about ssh:// URLs.
393 about ssh:// URLs.
394 """
394 """
395 cmdutil.setremoteconfig(ui, opts)
395 cmdutil.setremoteconfig(ui, opts)
396 hg.clone(ui, source, dest,
396 hg.clone(ui, source, dest,
397 pull=opts['pull'],
397 pull=opts['pull'],
398 stream=opts['uncompressed'],
398 stream=opts['uncompressed'],
399 rev=opts['rev'],
399 rev=opts['rev'],
400 update=not opts['noupdate'])
400 update=not opts['noupdate'])
401
401
402 def commit(ui, repo, *pats, **opts):
402 def commit(ui, repo, *pats, **opts):
403 """commit the specified files or all outstanding changes
403 """commit the specified files or all outstanding changes
404
404
405 Commit changes to the given files into the repository.
405 Commit changes to the given files into the repository.
406
406
407 If a list of files is omitted, all changes reported by "hg status"
407 If a list of files is omitted, all changes reported by "hg status"
408 will be committed.
408 will be committed.
409
409
410 If no commit message is specified, the editor configured in your hgrc
410 If no commit message is specified, the editor configured in your hgrc
411 or in the EDITOR environment variable is started to enter a message.
411 or in the EDITOR environment variable is started to enter a message.
412 """
412 """
413 message = cmdutil.logmessage(opts)
413 message = cmdutil.logmessage(opts)
414
414
415 if opts['addremove']:
415 if opts['addremove']:
416 cmdutil.addremove(repo, pats, opts)
416 cmdutil.addremove(repo, pats, opts)
417 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
417 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
418 if pats:
418 if pats:
419 status = repo.status(files=fns, match=match)
419 status = repo.status(files=fns, match=match)
420 modified, added, removed, deleted, unknown = status[:5]
420 modified, added, removed, deleted, unknown = status[:5]
421 files = modified + added + removed
421 files = modified + added + removed
422 slist = None
422 slist = None
423 for f in fns:
423 for f in fns:
424 if f == '.':
424 if f == '.':
425 continue
425 continue
426 if f not in files:
426 if f not in files:
427 rf = repo.wjoin(f)
427 rf = repo.wjoin(f)
428 try:
428 try:
429 mode = os.lstat(rf)[stat.ST_MODE]
429 mode = os.lstat(rf)[stat.ST_MODE]
430 except OSError:
430 except OSError:
431 raise util.Abort(_("file %s not found!") % rf)
431 raise util.Abort(_("file %s not found!") % rf)
432 if stat.S_ISDIR(mode):
432 if stat.S_ISDIR(mode):
433 name = f + '/'
433 name = f + '/'
434 if slist is None:
434 if slist is None:
435 slist = list(files)
435 slist = list(files)
436 slist.sort()
436 slist.sort()
437 i = bisect.bisect(slist, name)
437 i = bisect.bisect(slist, name)
438 if i >= len(slist) or not slist[i].startswith(name):
438 if i >= len(slist) or not slist[i].startswith(name):
439 raise util.Abort(_("no match under directory %s!")
439 raise util.Abort(_("no match under directory %s!")
440 % rf)
440 % rf)
441 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
441 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
442 raise util.Abort(_("can't commit %s: "
442 raise util.Abort(_("can't commit %s: "
443 "unsupported file type!") % rf)
443 "unsupported file type!") % rf)
444 elif repo.dirstate.state(f) == '?':
444 elif repo.dirstate.state(f) == '?':
445 raise util.Abort(_("file %s not tracked!") % rf)
445 raise util.Abort(_("file %s not tracked!") % rf)
446 else:
446 else:
447 files = []
447 files = []
448 try:
448 try:
449 repo.commit(files, message, opts['user'], opts['date'], match,
449 repo.commit(files, message, opts['user'], opts['date'], match,
450 force_editor=opts.get('force_editor'))
450 force_editor=opts.get('force_editor'))
451 except ValueError, inst:
451 except ValueError, inst:
452 raise util.Abort(str(inst))
452 raise util.Abort(str(inst))
453
453
454 def docopy(ui, repo, pats, opts, wlock):
454 def docopy(ui, repo, pats, opts, wlock):
455 # called with the repo lock held
455 # called with the repo lock held
456 #
456 #
457 # hgsep => pathname that uses "/" to separate directories
457 # hgsep => pathname that uses "/" to separate directories
458 # ossep => pathname that uses os.sep to separate directories
458 # ossep => pathname that uses os.sep to separate directories
459 cwd = repo.getcwd()
459 cwd = repo.getcwd()
460 errors = 0
460 errors = 0
461 copied = []
461 copied = []
462 targets = {}
462 targets = {}
463
463
464 # abs: hgsep
464 # abs: hgsep
465 # rel: ossep
465 # rel: ossep
466 # return: hgsep
466 # return: hgsep
467 def okaytocopy(abs, rel, exact):
467 def okaytocopy(abs, rel, exact):
468 reasons = {'?': _('is not managed'),
468 reasons = {'?': _('is not managed'),
469 'a': _('has been marked for add'),
470 'r': _('has been marked for remove')}
469 'r': _('has been marked for remove')}
471 state = repo.dirstate.state(abs)
470 state = repo.dirstate.state(abs)
472 reason = reasons.get(state)
471 reason = reasons.get(state)
473 if reason:
472 if reason:
473 if exact:
474 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
475 else:
474 if state == 'a':
476 if state == 'a':
475 origsrc = repo.dirstate.copied(abs)
477 origsrc = repo.dirstate.copied(abs)
476 if origsrc is not None:
478 if origsrc is not None:
477 return origsrc
479 return origsrc
478 if exact:
479 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
480 else:
481 return abs
480 return abs
482
481
483 # origsrc: hgsep
482 # origsrc: hgsep
484 # abssrc: hgsep
483 # abssrc: hgsep
485 # relsrc: ossep
484 # relsrc: ossep
486 # otarget: ossep
485 # otarget: ossep
487 def copy(origsrc, abssrc, relsrc, otarget, exact):
486 def copy(origsrc, abssrc, relsrc, otarget, exact):
488 abstarget = util.canonpath(repo.root, cwd, otarget)
487 abstarget = util.canonpath(repo.root, cwd, otarget)
489 reltarget = repo.pathto(abstarget, cwd)
488 reltarget = repo.pathto(abstarget, cwd)
490 prevsrc = targets.get(abstarget)
489 prevsrc = targets.get(abstarget)
491 src = repo.wjoin(abssrc)
490 src = repo.wjoin(abssrc)
492 target = repo.wjoin(abstarget)
491 target = repo.wjoin(abstarget)
493 if prevsrc is not None:
492 if prevsrc is not None:
494 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
493 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
495 (reltarget, repo.pathto(abssrc, cwd),
494 (reltarget, repo.pathto(abssrc, cwd),
496 repo.pathto(prevsrc, cwd)))
495 repo.pathto(prevsrc, cwd)))
497 return
496 return
498 if (not opts['after'] and os.path.exists(target) or
497 if (not opts['after'] and os.path.exists(target) or
499 opts['after'] and repo.dirstate.state(abstarget) not in '?ar'):
498 opts['after'] and repo.dirstate.state(abstarget) not in '?ar'):
500 if not opts['force']:
499 if not opts['force']:
501 ui.warn(_('%s: not overwriting - file exists\n') %
500 ui.warn(_('%s: not overwriting - file exists\n') %
502 reltarget)
501 reltarget)
503 return
502 return
504 if not opts['after'] and not opts.get('dry_run'):
503 if not opts['after'] and not opts.get('dry_run'):
505 os.unlink(target)
504 os.unlink(target)
506 if opts['after']:
505 if opts['after']:
507 if not os.path.exists(target):
506 if not os.path.exists(target):
508 return
507 return
509 else:
508 else:
510 targetdir = os.path.dirname(target) or '.'
509 targetdir = os.path.dirname(target) or '.'
511 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
510 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
512 os.makedirs(targetdir)
511 os.makedirs(targetdir)
513 try:
512 try:
514 restore = repo.dirstate.state(abstarget) == 'r'
513 restore = repo.dirstate.state(abstarget) == 'r'
515 if restore and not opts.get('dry_run'):
514 if restore and not opts.get('dry_run'):
516 repo.undelete([abstarget], wlock)
515 repo.undelete([abstarget], wlock)
517 try:
516 try:
518 if not opts.get('dry_run'):
517 if not opts.get('dry_run'):
519 util.copyfile(src, target)
518 util.copyfile(src, target)
520 restore = False
519 restore = False
521 finally:
520 finally:
522 if restore:
521 if restore:
523 repo.remove([abstarget], wlock=wlock)
522 repo.remove([abstarget], wlock=wlock)
524 except IOError, inst:
523 except IOError, inst:
525 if inst.errno == errno.ENOENT:
524 if inst.errno == errno.ENOENT:
526 ui.warn(_('%s: deleted in working copy\n') % relsrc)
525 ui.warn(_('%s: deleted in working copy\n') % relsrc)
527 else:
526 else:
528 ui.warn(_('%s: cannot copy - %s\n') %
527 ui.warn(_('%s: cannot copy - %s\n') %
529 (relsrc, inst.strerror))
528 (relsrc, inst.strerror))
530 errors += 1
529 errors += 1
531 return
530 return
532 if ui.verbose or not exact:
531 if ui.verbose or not exact:
533 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
532 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
534 targets[abstarget] = abssrc
533 targets[abstarget] = abssrc
535 if abstarget != origsrc and not opts.get('dry_run'):
534 if abstarget != origsrc:
535 if repo.dirstate.state(origsrc) == 'a':
536 ui.warn(_("%s was marked for addition. "
537 "%s will not be committed as a copy.\n")
538 % (repo.pathto(origsrc, cwd), reltarget))
539 if abstarget not in repo.dirstate and not opts.get('dry_run'):
540 repo.add([abstarget], wlock)
541 elif not opts.get('dry_run'):
536 repo.copy(origsrc, abstarget, wlock)
542 repo.copy(origsrc, abstarget, wlock)
537 copied.append((abssrc, relsrc, exact))
543 copied.append((abssrc, relsrc, exact))
538
544
539 # pat: ossep
545 # pat: ossep
540 # dest ossep
546 # dest ossep
541 # srcs: list of (hgsep, hgsep, ossep, bool)
547 # srcs: list of (hgsep, hgsep, ossep, bool)
542 # return: function that takes hgsep and returns ossep
548 # return: function that takes hgsep and returns ossep
543 def targetpathfn(pat, dest, srcs):
549 def targetpathfn(pat, dest, srcs):
544 if os.path.isdir(pat):
550 if os.path.isdir(pat):
545 abspfx = util.canonpath(repo.root, cwd, pat)
551 abspfx = util.canonpath(repo.root, cwd, pat)
546 abspfx = util.localpath(abspfx)
552 abspfx = util.localpath(abspfx)
547 if destdirexists:
553 if destdirexists:
548 striplen = len(os.path.split(abspfx)[0])
554 striplen = len(os.path.split(abspfx)[0])
549 else:
555 else:
550 striplen = len(abspfx)
556 striplen = len(abspfx)
551 if striplen:
557 if striplen:
552 striplen += len(os.sep)
558 striplen += len(os.sep)
553 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
559 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
554 elif destdirexists:
560 elif destdirexists:
555 res = lambda p: os.path.join(dest,
561 res = lambda p: os.path.join(dest,
556 os.path.basename(util.localpath(p)))
562 os.path.basename(util.localpath(p)))
557 else:
563 else:
558 res = lambda p: dest
564 res = lambda p: dest
559 return res
565 return res
560
566
561 # pat: ossep
567 # pat: ossep
562 # dest ossep
568 # dest ossep
563 # srcs: list of (hgsep, hgsep, ossep, bool)
569 # srcs: list of (hgsep, hgsep, ossep, bool)
564 # return: function that takes hgsep and returns ossep
570 # return: function that takes hgsep and returns ossep
565 def targetpathafterfn(pat, dest, srcs):
571 def targetpathafterfn(pat, dest, srcs):
566 if util.patkind(pat, None)[0]:
572 if util.patkind(pat, None)[0]:
567 # a mercurial pattern
573 # a mercurial pattern
568 res = lambda p: os.path.join(dest,
574 res = lambda p: os.path.join(dest,
569 os.path.basename(util.localpath(p)))
575 os.path.basename(util.localpath(p)))
570 else:
576 else:
571 abspfx = util.canonpath(repo.root, cwd, pat)
577 abspfx = util.canonpath(repo.root, cwd, pat)
572 if len(abspfx) < len(srcs[0][0]):
578 if len(abspfx) < len(srcs[0][0]):
573 # A directory. Either the target path contains the last
579 # A directory. Either the target path contains the last
574 # component of the source path or it does not.
580 # component of the source path or it does not.
575 def evalpath(striplen):
581 def evalpath(striplen):
576 score = 0
582 score = 0
577 for s in srcs:
583 for s in srcs:
578 t = os.path.join(dest, util.localpath(s[0])[striplen:])
584 t = os.path.join(dest, util.localpath(s[0])[striplen:])
579 if os.path.exists(t):
585 if os.path.exists(t):
580 score += 1
586 score += 1
581 return score
587 return score
582
588
583 abspfx = util.localpath(abspfx)
589 abspfx = util.localpath(abspfx)
584 striplen = len(abspfx)
590 striplen = len(abspfx)
585 if striplen:
591 if striplen:
586 striplen += len(os.sep)
592 striplen += len(os.sep)
587 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
593 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
588 score = evalpath(striplen)
594 score = evalpath(striplen)
589 striplen1 = len(os.path.split(abspfx)[0])
595 striplen1 = len(os.path.split(abspfx)[0])
590 if striplen1:
596 if striplen1:
591 striplen1 += len(os.sep)
597 striplen1 += len(os.sep)
592 if evalpath(striplen1) > score:
598 if evalpath(striplen1) > score:
593 striplen = striplen1
599 striplen = striplen1
594 res = lambda p: os.path.join(dest,
600 res = lambda p: os.path.join(dest,
595 util.localpath(p)[striplen:])
601 util.localpath(p)[striplen:])
596 else:
602 else:
597 # a file
603 # a file
598 if destdirexists:
604 if destdirexists:
599 res = lambda p: os.path.join(dest,
605 res = lambda p: os.path.join(dest,
600 os.path.basename(util.localpath(p)))
606 os.path.basename(util.localpath(p)))
601 else:
607 else:
602 res = lambda p: dest
608 res = lambda p: dest
603 return res
609 return res
604
610
605
611
606 pats = util.expand_glob(pats)
612 pats = util.expand_glob(pats)
607 if not pats:
613 if not pats:
608 raise util.Abort(_('no source or destination specified'))
614 raise util.Abort(_('no source or destination specified'))
609 if len(pats) == 1:
615 if len(pats) == 1:
610 raise util.Abort(_('no destination specified'))
616 raise util.Abort(_('no destination specified'))
611 dest = pats.pop()
617 dest = pats.pop()
612 destdirexists = os.path.isdir(dest)
618 destdirexists = os.path.isdir(dest)
613 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
619 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
614 raise util.Abort(_('with multiple sources, destination must be an '
620 raise util.Abort(_('with multiple sources, destination must be an '
615 'existing directory'))
621 'existing directory'))
616 if opts['after']:
622 if opts['after']:
617 tfn = targetpathafterfn
623 tfn = targetpathafterfn
618 else:
624 else:
619 tfn = targetpathfn
625 tfn = targetpathfn
620 copylist = []
626 copylist = []
621 for pat in pats:
627 for pat in pats:
622 srcs = []
628 srcs = []
623 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
629 for tag, abssrc, relsrc, exact in cmdutil.walk(repo, [pat], opts,
624 globbed=True):
630 globbed=True):
625 origsrc = okaytocopy(abssrc, relsrc, exact)
631 origsrc = okaytocopy(abssrc, relsrc, exact)
626 if origsrc:
632 if origsrc:
627 srcs.append((origsrc, abssrc, relsrc, exact))
633 srcs.append((origsrc, abssrc, relsrc, exact))
628 if not srcs:
634 if not srcs:
629 continue
635 continue
630 copylist.append((tfn(pat, dest, srcs), srcs))
636 copylist.append((tfn(pat, dest, srcs), srcs))
631 if not copylist:
637 if not copylist:
632 raise util.Abort(_('no files to copy'))
638 raise util.Abort(_('no files to copy'))
633
639
634 for targetpath, srcs in copylist:
640 for targetpath, srcs in copylist:
635 for origsrc, abssrc, relsrc, exact in srcs:
641 for origsrc, abssrc, relsrc, exact in srcs:
636 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
642 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
637
643
638 if errors:
644 if errors:
639 ui.warn(_('(consider using --after)\n'))
645 ui.warn(_('(consider using --after)\n'))
640 return errors, copied
646 return errors, copied
641
647
642 def copy(ui, repo, *pats, **opts):
648 def copy(ui, repo, *pats, **opts):
643 """mark files as copied for the next commit
649 """mark files as copied for the next commit
644
650
645 Mark dest as having copies of source files. If dest is a
651 Mark dest as having copies of source files. If dest is a
646 directory, copies are put in that directory. If dest is a file,
652 directory, copies are put in that directory. If dest is a file,
647 there can only be one source.
653 there can only be one source.
648
654
649 By default, this command copies the contents of files as they
655 By default, this command copies the contents of files as they
650 stand in the working directory. If invoked with --after, the
656 stand in the working directory. If invoked with --after, the
651 operation is recorded, but no copying is performed.
657 operation is recorded, but no copying is performed.
652
658
653 This command takes effect in the next commit. To undo a copy
659 This command takes effect in the next commit. To undo a copy
654 before that, see hg revert.
660 before that, see hg revert.
655 """
661 """
656 wlock = repo.wlock(0)
662 wlock = repo.wlock(0)
657 errs, copied = docopy(ui, repo, pats, opts, wlock)
663 errs, copied = docopy(ui, repo, pats, opts, wlock)
658 return errs
664 return errs
659
665
660 def debugancestor(ui, index, rev1, rev2):
666 def debugancestor(ui, index, rev1, rev2):
661 """find the ancestor revision of two revisions in a given index"""
667 """find the ancestor revision of two revisions in a given index"""
662 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
668 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
663 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
669 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
664 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
670 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
665
671
666 def debugcomplete(ui, cmd='', **opts):
672 def debugcomplete(ui, cmd='', **opts):
667 """returns the completion list associated with the given command"""
673 """returns the completion list associated with the given command"""
668
674
669 if opts['options']:
675 if opts['options']:
670 options = []
676 options = []
671 otables = [globalopts]
677 otables = [globalopts]
672 if cmd:
678 if cmd:
673 aliases, entry = cmdutil.findcmd(ui, cmd)
679 aliases, entry = cmdutil.findcmd(ui, cmd)
674 otables.append(entry[1])
680 otables.append(entry[1])
675 for t in otables:
681 for t in otables:
676 for o in t:
682 for o in t:
677 if o[0]:
683 if o[0]:
678 options.append('-%s' % o[0])
684 options.append('-%s' % o[0])
679 options.append('--%s' % o[1])
685 options.append('--%s' % o[1])
680 ui.write("%s\n" % "\n".join(options))
686 ui.write("%s\n" % "\n".join(options))
681 return
687 return
682
688
683 clist = cmdutil.findpossible(ui, cmd).keys()
689 clist = cmdutil.findpossible(ui, cmd).keys()
684 clist.sort()
690 clist.sort()
685 ui.write("%s\n" % "\n".join(clist))
691 ui.write("%s\n" % "\n".join(clist))
686
692
687 def debugrebuildstate(ui, repo, rev=""):
693 def debugrebuildstate(ui, repo, rev=""):
688 """rebuild the dirstate as it would look like for the given revision"""
694 """rebuild the dirstate as it would look like for the given revision"""
689 if rev == "":
695 if rev == "":
690 rev = repo.changelog.tip()
696 rev = repo.changelog.tip()
691 ctx = repo.changectx(rev)
697 ctx = repo.changectx(rev)
692 files = ctx.manifest()
698 files = ctx.manifest()
693 wlock = repo.wlock()
699 wlock = repo.wlock()
694 repo.dirstate.rebuild(rev, files)
700 repo.dirstate.rebuild(rev, files)
695
701
696 def debugcheckstate(ui, repo):
702 def debugcheckstate(ui, repo):
697 """validate the correctness of the current dirstate"""
703 """validate the correctness of the current dirstate"""
698 parent1, parent2 = repo.dirstate.parents()
704 parent1, parent2 = repo.dirstate.parents()
699 dc = repo.dirstate
705 dc = repo.dirstate
700 m1 = repo.changectx(parent1).manifest()
706 m1 = repo.changectx(parent1).manifest()
701 m2 = repo.changectx(parent2).manifest()
707 m2 = repo.changectx(parent2).manifest()
702 errors = 0
708 errors = 0
703 for f in dc:
709 for f in dc:
704 state = repo.dirstate.state(f)
710 state = repo.dirstate.state(f)
705 if state in "nr" and f not in m1:
711 if state in "nr" and f not in m1:
706 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
712 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
707 errors += 1
713 errors += 1
708 if state in "a" and f in m1:
714 if state in "a" and f in m1:
709 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
715 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
710 errors += 1
716 errors += 1
711 if state in "m" and f not in m1 and f not in m2:
717 if state in "m" and f not in m1 and f not in m2:
712 ui.warn(_("%s in state %s, but not in either manifest\n") %
718 ui.warn(_("%s in state %s, but not in either manifest\n") %
713 (f, state))
719 (f, state))
714 errors += 1
720 errors += 1
715 for f in m1:
721 for f in m1:
716 state = repo.dirstate.state(f)
722 state = repo.dirstate.state(f)
717 if state not in "nrm":
723 if state not in "nrm":
718 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
724 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
719 errors += 1
725 errors += 1
720 if errors:
726 if errors:
721 error = _(".hg/dirstate inconsistent with current parent's manifest")
727 error = _(".hg/dirstate inconsistent with current parent's manifest")
722 raise util.Abort(error)
728 raise util.Abort(error)
723
729
724 def showconfig(ui, repo, *values, **opts):
730 def showconfig(ui, repo, *values, **opts):
725 """show combined config settings from all hgrc files
731 """show combined config settings from all hgrc files
726
732
727 With no args, print names and values of all config items.
733 With no args, print names and values of all config items.
728
734
729 With one arg of the form section.name, print just the value of
735 With one arg of the form section.name, print just the value of
730 that config item.
736 that config item.
731
737
732 With multiple args, print names and values of all config items
738 With multiple args, print names and values of all config items
733 with matching section names."""
739 with matching section names."""
734
740
735 untrusted = bool(opts.get('untrusted'))
741 untrusted = bool(opts.get('untrusted'))
736 if values:
742 if values:
737 if len([v for v in values if '.' in v]) > 1:
743 if len([v for v in values if '.' in v]) > 1:
738 raise util.Abort(_('only one config item permitted'))
744 raise util.Abort(_('only one config item permitted'))
739 for section, name, value in ui.walkconfig(untrusted=untrusted):
745 for section, name, value in ui.walkconfig(untrusted=untrusted):
740 sectname = section + '.' + name
746 sectname = section + '.' + name
741 if values:
747 if values:
742 for v in values:
748 for v in values:
743 if v == section:
749 if v == section:
744 ui.write('%s=%s\n' % (sectname, value))
750 ui.write('%s=%s\n' % (sectname, value))
745 elif v == sectname:
751 elif v == sectname:
746 ui.write(value, '\n')
752 ui.write(value, '\n')
747 else:
753 else:
748 ui.write('%s=%s\n' % (sectname, value))
754 ui.write('%s=%s\n' % (sectname, value))
749
755
750 def debugsetparents(ui, repo, rev1, rev2=None):
756 def debugsetparents(ui, repo, rev1, rev2=None):
751 """manually set the parents of the current working directory
757 """manually set the parents of the current working directory
752
758
753 This is useful for writing repository conversion tools, but should
759 This is useful for writing repository conversion tools, but should
754 be used with care.
760 be used with care.
755 """
761 """
756
762
757 if not rev2:
763 if not rev2:
758 rev2 = hex(nullid)
764 rev2 = hex(nullid)
759
765
760 wlock = repo.wlock()
766 wlock = repo.wlock()
761 try:
767 try:
762 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
768 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
763 finally:
769 finally:
764 wlock.release()
770 wlock.release()
765
771
766 def debugstate(ui, repo):
772 def debugstate(ui, repo):
767 """show the contents of the current dirstate"""
773 """show the contents of the current dirstate"""
768 dc = repo.dirstate
774 dc = repo.dirstate
769 for file_ in dc:
775 for file_ in dc:
770 if dc[file_][3] == -1:
776 if dc[file_][3] == -1:
771 # Pad or slice to locale representation
777 # Pad or slice to locale representation
772 locale_len = len(time.strftime("%x %X", time.localtime(0)))
778 locale_len = len(time.strftime("%x %X", time.localtime(0)))
773 timestr = 'unset'
779 timestr = 'unset'
774 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
780 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
775 else:
781 else:
776 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
782 timestr = time.strftime("%x %X", time.localtime(dc[file_][3]))
777 ui.write("%c %3o %10d %s %s\n"
783 ui.write("%c %3o %10d %s %s\n"
778 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
784 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
779 timestr, file_))
785 timestr, file_))
780 for f in repo.dirstate.copies():
786 for f in repo.dirstate.copies():
781 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
787 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
782
788
783 def debugdata(ui, file_, rev):
789 def debugdata(ui, file_, rev):
784 """dump the contents of a data file revision"""
790 """dump the contents of a data file revision"""
785 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
791 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
786 try:
792 try:
787 ui.write(r.revision(r.lookup(rev)))
793 ui.write(r.revision(r.lookup(rev)))
788 except KeyError:
794 except KeyError:
789 raise util.Abort(_('invalid revision identifier %s') % rev)
795 raise util.Abort(_('invalid revision identifier %s') % rev)
790
796
791 def debugdate(ui, date, range=None, **opts):
797 def debugdate(ui, date, range=None, **opts):
792 """parse and display a date"""
798 """parse and display a date"""
793 if opts["extended"]:
799 if opts["extended"]:
794 d = util.parsedate(date, util.extendeddateformats)
800 d = util.parsedate(date, util.extendeddateformats)
795 else:
801 else:
796 d = util.parsedate(date)
802 d = util.parsedate(date)
797 ui.write("internal: %s %s\n" % d)
803 ui.write("internal: %s %s\n" % d)
798 ui.write("standard: %s\n" % util.datestr(d))
804 ui.write("standard: %s\n" % util.datestr(d))
799 if range:
805 if range:
800 m = util.matchdate(range)
806 m = util.matchdate(range)
801 ui.write("match: %s\n" % m(d[0]))
807 ui.write("match: %s\n" % m(d[0]))
802
808
803 def debugindex(ui, file_):
809 def debugindex(ui, file_):
804 """dump the contents of an index file"""
810 """dump the contents of an index file"""
805 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
811 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
806 ui.write(" rev offset length base linkrev" +
812 ui.write(" rev offset length base linkrev" +
807 " nodeid p1 p2\n")
813 " nodeid p1 p2\n")
808 for i in xrange(r.count()):
814 for i in xrange(r.count()):
809 node = r.node(i)
815 node = r.node(i)
810 pp = r.parents(node)
816 pp = r.parents(node)
811 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
817 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
812 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
818 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
813 short(node), short(pp[0]), short(pp[1])))
819 short(node), short(pp[0]), short(pp[1])))
814
820
815 def debugindexdot(ui, file_):
821 def debugindexdot(ui, file_):
816 """dump an index DAG as a .dot file"""
822 """dump an index DAG as a .dot file"""
817 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
823 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
818 ui.write("digraph G {\n")
824 ui.write("digraph G {\n")
819 for i in xrange(r.count()):
825 for i in xrange(r.count()):
820 node = r.node(i)
826 node = r.node(i)
821 pp = r.parents(node)
827 pp = r.parents(node)
822 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
828 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
823 if pp[1] != nullid:
829 if pp[1] != nullid:
824 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
830 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
825 ui.write("}\n")
831 ui.write("}\n")
826
832
827 def debuginstall(ui):
833 def debuginstall(ui):
828 '''test Mercurial installation'''
834 '''test Mercurial installation'''
829
835
830 def writetemp(contents):
836 def writetemp(contents):
831 (fd, name) = tempfile.mkstemp()
837 (fd, name) = tempfile.mkstemp()
832 f = os.fdopen(fd, "wb")
838 f = os.fdopen(fd, "wb")
833 f.write(contents)
839 f.write(contents)
834 f.close()
840 f.close()
835 return name
841 return name
836
842
837 problems = 0
843 problems = 0
838
844
839 # encoding
845 # encoding
840 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
846 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
841 try:
847 try:
842 util.fromlocal("test")
848 util.fromlocal("test")
843 except util.Abort, inst:
849 except util.Abort, inst:
844 ui.write(" %s\n" % inst)
850 ui.write(" %s\n" % inst)
845 ui.write(_(" (check that your locale is properly set)\n"))
851 ui.write(_(" (check that your locale is properly set)\n"))
846 problems += 1
852 problems += 1
847
853
848 # compiled modules
854 # compiled modules
849 ui.status(_("Checking extensions...\n"))
855 ui.status(_("Checking extensions...\n"))
850 try:
856 try:
851 import bdiff, mpatch, base85
857 import bdiff, mpatch, base85
852 except Exception, inst:
858 except Exception, inst:
853 ui.write(" %s\n" % inst)
859 ui.write(" %s\n" % inst)
854 ui.write(_(" One or more extensions could not be found"))
860 ui.write(_(" One or more extensions could not be found"))
855 ui.write(_(" (check that you compiled the extensions)\n"))
861 ui.write(_(" (check that you compiled the extensions)\n"))
856 problems += 1
862 problems += 1
857
863
858 # templates
864 # templates
859 ui.status(_("Checking templates...\n"))
865 ui.status(_("Checking templates...\n"))
860 try:
866 try:
861 import templater
867 import templater
862 t = templater.templater(templater.templatepath("map-cmdline.default"))
868 t = templater.templater(templater.templatepath("map-cmdline.default"))
863 except Exception, inst:
869 except Exception, inst:
864 ui.write(" %s\n" % inst)
870 ui.write(" %s\n" % inst)
865 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
871 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
866 problems += 1
872 problems += 1
867
873
868 # patch
874 # patch
869 ui.status(_("Checking patch...\n"))
875 ui.status(_("Checking patch...\n"))
870 patcher = ui.config('ui', 'patch')
876 patcher = ui.config('ui', 'patch')
871 patcher = ((patcher and util.find_exe(patcher)) or
877 patcher = ((patcher and util.find_exe(patcher)) or
872 util.find_exe('gpatch') or
878 util.find_exe('gpatch') or
873 util.find_exe('patch'))
879 util.find_exe('patch'))
874 if not patcher:
880 if not patcher:
875 ui.write(_(" Can't find patch or gpatch in PATH\n"))
881 ui.write(_(" Can't find patch or gpatch in PATH\n"))
876 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
882 ui.write(_(" (specify a patch utility in your .hgrc file)\n"))
877 problems += 1
883 problems += 1
878 else:
884 else:
879 # actually attempt a patch here
885 # actually attempt a patch here
880 a = "1\n2\n3\n4\n"
886 a = "1\n2\n3\n4\n"
881 b = "1\n2\n3\ninsert\n4\n"
887 b = "1\n2\n3\ninsert\n4\n"
882 fa = writetemp(a)
888 fa = writetemp(a)
883 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa))
889 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa))
884 fd = writetemp(d)
890 fd = writetemp(d)
885
891
886 files = {}
892 files = {}
887 try:
893 try:
888 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
894 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
889 except util.Abort, e:
895 except util.Abort, e:
890 ui.write(_(" patch call failed:\n"))
896 ui.write(_(" patch call failed:\n"))
891 ui.write(" " + str(e) + "\n")
897 ui.write(" " + str(e) + "\n")
892 problems += 1
898 problems += 1
893 else:
899 else:
894 if list(files) != [os.path.basename(fa)]:
900 if list(files) != [os.path.basename(fa)]:
895 ui.write(_(" unexpected patch output!"))
901 ui.write(_(" unexpected patch output!"))
896 ui.write(_(" (you may have an incompatible version of patch)\n"))
902 ui.write(_(" (you may have an incompatible version of patch)\n"))
897 problems += 1
903 problems += 1
898 a = file(fa).read()
904 a = file(fa).read()
899 if a != b:
905 if a != b:
900 ui.write(_(" patch test failed!"))
906 ui.write(_(" patch test failed!"))
901 ui.write(_(" (you may have an incompatible version of patch)\n"))
907 ui.write(_(" (you may have an incompatible version of patch)\n"))
902 problems += 1
908 problems += 1
903
909
904 os.unlink(fa)
910 os.unlink(fa)
905 os.unlink(fd)
911 os.unlink(fd)
906
912
907 # merge helper
913 # merge helper
908 ui.status(_("Checking merge helper...\n"))
914 ui.status(_("Checking merge helper...\n"))
909 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
915 cmd = (os.environ.get("HGMERGE") or ui.config("ui", "merge")
910 or "hgmerge")
916 or "hgmerge")
911 cmdpath = util.find_exe(cmd) or util.find_exe(cmd.split()[0])
917 cmdpath = util.find_exe(cmd) or util.find_exe(cmd.split()[0])
912 if not cmdpath:
918 if not cmdpath:
913 if cmd == 'hgmerge':
919 if cmd == 'hgmerge':
914 ui.write(_(" No merge helper set and can't find default"
920 ui.write(_(" No merge helper set and can't find default"
915 " hgmerge script in PATH\n"))
921 " hgmerge script in PATH\n"))
916 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
922 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
917 else:
923 else:
918 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
924 ui.write(_(" Can't find merge helper '%s' in PATH\n") % cmd)
919 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
925 ui.write(_(" (specify a merge helper in your .hgrc file)\n"))
920 problems += 1
926 problems += 1
921 else:
927 else:
922 # actually attempt a patch here
928 # actually attempt a patch here
923 fa = writetemp("1\n2\n3\n4\n")
929 fa = writetemp("1\n2\n3\n4\n")
924 fl = writetemp("1\n2\n3\ninsert\n4\n")
930 fl = writetemp("1\n2\n3\ninsert\n4\n")
925 fr = writetemp("begin\n1\n2\n3\n4\n")
931 fr = writetemp("begin\n1\n2\n3\n4\n")
926 r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
932 r = os.system('%s %s %s %s' % (cmd, fl, fa, fr))
927 if r:
933 if r:
928 ui.write(_(" got unexpected merge error %d!") % r)
934 ui.write(_(" got unexpected merge error %d!") % r)
929 problems += 1
935 problems += 1
930 m = file(fl).read()
936 m = file(fl).read()
931 if m != "begin\n1\n2\n3\ninsert\n4\n":
937 if m != "begin\n1\n2\n3\ninsert\n4\n":
932 ui.write(_(" got unexpected merge results!") % r)
938 ui.write(_(" got unexpected merge results!") % r)
933 ui.write(_(" (your merge helper may have the"
939 ui.write(_(" (your merge helper may have the"
934 " wrong argument order)\n"))
940 " wrong argument order)\n"))
935 ui.write(m)
941 ui.write(m)
936 os.unlink(fa)
942 os.unlink(fa)
937 os.unlink(fl)
943 os.unlink(fl)
938 os.unlink(fr)
944 os.unlink(fr)
939
945
940 # editor
946 # editor
941 ui.status(_("Checking commit editor...\n"))
947 ui.status(_("Checking commit editor...\n"))
942 editor = (os.environ.get("HGEDITOR") or
948 editor = (os.environ.get("HGEDITOR") or
943 ui.config("ui", "editor") or
949 ui.config("ui", "editor") or
944 os.environ.get("EDITOR", "vi"))
950 os.environ.get("EDITOR", "vi"))
945 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
951 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
946 if not cmdpath:
952 if not cmdpath:
947 if editor == 'vi':
953 if editor == 'vi':
948 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
954 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
949 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
955 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
950 else:
956 else:
951 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
957 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
952 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
958 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
953 problems += 1
959 problems += 1
954
960
955 # check username
961 # check username
956 ui.status(_("Checking username...\n"))
962 ui.status(_("Checking username...\n"))
957 user = os.environ.get("HGUSER")
963 user = os.environ.get("HGUSER")
958 if user is None:
964 if user is None:
959 user = ui.config("ui", "username")
965 user = ui.config("ui", "username")
960 if user is None:
966 if user is None:
961 user = os.environ.get("EMAIL")
967 user = os.environ.get("EMAIL")
962 if not user:
968 if not user:
963 ui.warn(" ")
969 ui.warn(" ")
964 ui.username()
970 ui.username()
965 ui.write(_(" (specify a username in your .hgrc file)\n"))
971 ui.write(_(" (specify a username in your .hgrc file)\n"))
966
972
967 if not problems:
973 if not problems:
968 ui.status(_("No problems detected\n"))
974 ui.status(_("No problems detected\n"))
969 else:
975 else:
970 ui.write(_("%s problems detected,"
976 ui.write(_("%s problems detected,"
971 " please check your install!\n") % problems)
977 " please check your install!\n") % problems)
972
978
973 return problems
979 return problems
974
980
975 def debugrename(ui, repo, file1, *pats, **opts):
981 def debugrename(ui, repo, file1, *pats, **opts):
976 """dump rename information"""
982 """dump rename information"""
977
983
978 ctx = repo.changectx(opts.get('rev', 'tip'))
984 ctx = repo.changectx(opts.get('rev', 'tip'))
979 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
985 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
980 ctx.node()):
986 ctx.node()):
981 m = ctx.filectx(abs).renamed()
987 m = ctx.filectx(abs).renamed()
982 if m:
988 if m:
983 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
989 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
984 else:
990 else:
985 ui.write(_("%s not renamed\n") % rel)
991 ui.write(_("%s not renamed\n") % rel)
986
992
987 def debugwalk(ui, repo, *pats, **opts):
993 def debugwalk(ui, repo, *pats, **opts):
988 """show how files match on given patterns"""
994 """show how files match on given patterns"""
989 items = list(cmdutil.walk(repo, pats, opts))
995 items = list(cmdutil.walk(repo, pats, opts))
990 if not items:
996 if not items:
991 return
997 return
992 fmt = '%%s %%-%ds %%-%ds %%s' % (
998 fmt = '%%s %%-%ds %%-%ds %%s' % (
993 max([len(abs) for (src, abs, rel, exact) in items]),
999 max([len(abs) for (src, abs, rel, exact) in items]),
994 max([len(rel) for (src, abs, rel, exact) in items]))
1000 max([len(rel) for (src, abs, rel, exact) in items]))
995 for src, abs, rel, exact in items:
1001 for src, abs, rel, exact in items:
996 line = fmt % (src, abs, rel, exact and 'exact' or '')
1002 line = fmt % (src, abs, rel, exact and 'exact' or '')
997 ui.write("%s\n" % line.rstrip())
1003 ui.write("%s\n" % line.rstrip())
998
1004
999 def diff(ui, repo, *pats, **opts):
1005 def diff(ui, repo, *pats, **opts):
1000 """diff repository (or selected files)
1006 """diff repository (or selected files)
1001
1007
1002 Show differences between revisions for the specified files.
1008 Show differences between revisions for the specified files.
1003
1009
1004 Differences between files are shown using the unified diff format.
1010 Differences between files are shown using the unified diff format.
1005
1011
1006 NOTE: diff may generate unexpected results for merges, as it will
1012 NOTE: diff may generate unexpected results for merges, as it will
1007 default to comparing against the working directory's first parent
1013 default to comparing against the working directory's first parent
1008 changeset if no revisions are specified.
1014 changeset if no revisions are specified.
1009
1015
1010 When two revision arguments are given, then changes are shown
1016 When two revision arguments are given, then changes are shown
1011 between those revisions. If only one revision is specified then
1017 between those revisions. If only one revision is specified then
1012 that revision is compared to the working directory, and, when no
1018 that revision is compared to the working directory, and, when no
1013 revisions are specified, the working directory files are compared
1019 revisions are specified, the working directory files are compared
1014 to its parent.
1020 to its parent.
1015
1021
1016 Without the -a option, diff will avoid generating diffs of files
1022 Without the -a option, diff will avoid generating diffs of files
1017 it detects as binary. With -a, diff will generate a diff anyway,
1023 it detects as binary. With -a, diff will generate a diff anyway,
1018 probably with undesirable results.
1024 probably with undesirable results.
1019 """
1025 """
1020 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1026 node1, node2 = cmdutil.revpair(repo, opts['rev'])
1021
1027
1022 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1028 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1023
1029
1024 patch.diff(repo, node1, node2, fns, match=matchfn,
1030 patch.diff(repo, node1, node2, fns, match=matchfn,
1025 opts=patch.diffopts(ui, opts))
1031 opts=patch.diffopts(ui, opts))
1026
1032
1027 def export(ui, repo, *changesets, **opts):
1033 def export(ui, repo, *changesets, **opts):
1028 """dump the header and diffs for one or more changesets
1034 """dump the header and diffs for one or more changesets
1029
1035
1030 Print the changeset header and diffs for one or more revisions.
1036 Print the changeset header and diffs for one or more revisions.
1031
1037
1032 The information shown in the changeset header is: author,
1038 The information shown in the changeset header is: author,
1033 changeset hash, parent(s) and commit comment.
1039 changeset hash, parent(s) and commit comment.
1034
1040
1035 NOTE: export may generate unexpected diff output for merge changesets,
1041 NOTE: export may generate unexpected diff output for merge changesets,
1036 as it will compare the merge changeset against its first parent only.
1042 as it will compare the merge changeset against its first parent only.
1037
1043
1038 Output may be to a file, in which case the name of the file is
1044 Output may be to a file, in which case the name of the file is
1039 given using a format string. The formatting rules are as follows:
1045 given using a format string. The formatting rules are as follows:
1040
1046
1041 %% literal "%" character
1047 %% literal "%" character
1042 %H changeset hash (40 bytes of hexadecimal)
1048 %H changeset hash (40 bytes of hexadecimal)
1043 %N number of patches being generated
1049 %N number of patches being generated
1044 %R changeset revision number
1050 %R changeset revision number
1045 %b basename of the exporting repository
1051 %b basename of the exporting repository
1046 %h short-form changeset hash (12 bytes of hexadecimal)
1052 %h short-form changeset hash (12 bytes of hexadecimal)
1047 %n zero-padded sequence number, starting at 1
1053 %n zero-padded sequence number, starting at 1
1048 %r zero-padded changeset revision number
1054 %r zero-padded changeset revision number
1049
1055
1050 Without the -a option, export will avoid generating diffs of files
1056 Without the -a option, export will avoid generating diffs of files
1051 it detects as binary. With -a, export will generate a diff anyway,
1057 it detects as binary. With -a, export will generate a diff anyway,
1052 probably with undesirable results.
1058 probably with undesirable results.
1053
1059
1054 With the --switch-parent option, the diff will be against the second
1060 With the --switch-parent option, the diff will be against the second
1055 parent. It can be useful to review a merge.
1061 parent. It can be useful to review a merge.
1056 """
1062 """
1057 if not changesets:
1063 if not changesets:
1058 raise util.Abort(_("export requires at least one changeset"))
1064 raise util.Abort(_("export requires at least one changeset"))
1059 revs = cmdutil.revrange(repo, changesets)
1065 revs = cmdutil.revrange(repo, changesets)
1060 if len(revs) > 1:
1066 if len(revs) > 1:
1061 ui.note(_('exporting patches:\n'))
1067 ui.note(_('exporting patches:\n'))
1062 else:
1068 else:
1063 ui.note(_('exporting patch:\n'))
1069 ui.note(_('exporting patch:\n'))
1064 patch.export(repo, revs, template=opts['output'],
1070 patch.export(repo, revs, template=opts['output'],
1065 switch_parent=opts['switch_parent'],
1071 switch_parent=opts['switch_parent'],
1066 opts=patch.diffopts(ui, opts))
1072 opts=patch.diffopts(ui, opts))
1067
1073
1068 def grep(ui, repo, pattern, *pats, **opts):
1074 def grep(ui, repo, pattern, *pats, **opts):
1069 """search for a pattern in specified files and revisions
1075 """search for a pattern in specified files and revisions
1070
1076
1071 Search revisions of files for a regular expression.
1077 Search revisions of files for a regular expression.
1072
1078
1073 This command behaves differently than Unix grep. It only accepts
1079 This command behaves differently than Unix grep. It only accepts
1074 Python/Perl regexps. It searches repository history, not the
1080 Python/Perl regexps. It searches repository history, not the
1075 working directory. It always prints the revision number in which
1081 working directory. It always prints the revision number in which
1076 a match appears.
1082 a match appears.
1077
1083
1078 By default, grep only prints output for the first revision of a
1084 By default, grep only prints output for the first revision of a
1079 file in which it finds a match. To get it to print every revision
1085 file in which it finds a match. To get it to print every revision
1080 that contains a change in match status ("-" for a match that
1086 that contains a change in match status ("-" for a match that
1081 becomes a non-match, or "+" for a non-match that becomes a match),
1087 becomes a non-match, or "+" for a non-match that becomes a match),
1082 use the --all flag.
1088 use the --all flag.
1083 """
1089 """
1084 reflags = 0
1090 reflags = 0
1085 if opts['ignore_case']:
1091 if opts['ignore_case']:
1086 reflags |= re.I
1092 reflags |= re.I
1087 regexp = re.compile(pattern, reflags)
1093 regexp = re.compile(pattern, reflags)
1088 sep, eol = ':', '\n'
1094 sep, eol = ':', '\n'
1089 if opts['print0']:
1095 if opts['print0']:
1090 sep = eol = '\0'
1096 sep = eol = '\0'
1091
1097
1092 fcache = {}
1098 fcache = {}
1093 def getfile(fn):
1099 def getfile(fn):
1094 if fn not in fcache:
1100 if fn not in fcache:
1095 fcache[fn] = repo.file(fn)
1101 fcache[fn] = repo.file(fn)
1096 return fcache[fn]
1102 return fcache[fn]
1097
1103
1098 def matchlines(body):
1104 def matchlines(body):
1099 begin = 0
1105 begin = 0
1100 linenum = 0
1106 linenum = 0
1101 while True:
1107 while True:
1102 match = regexp.search(body, begin)
1108 match = regexp.search(body, begin)
1103 if not match:
1109 if not match:
1104 break
1110 break
1105 mstart, mend = match.span()
1111 mstart, mend = match.span()
1106 linenum += body.count('\n', begin, mstart) + 1
1112 linenum += body.count('\n', begin, mstart) + 1
1107 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1113 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1108 lend = body.find('\n', mend)
1114 lend = body.find('\n', mend)
1109 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1115 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1110 begin = lend + 1
1116 begin = lend + 1
1111
1117
1112 class linestate(object):
1118 class linestate(object):
1113 def __init__(self, line, linenum, colstart, colend):
1119 def __init__(self, line, linenum, colstart, colend):
1114 self.line = line
1120 self.line = line
1115 self.linenum = linenum
1121 self.linenum = linenum
1116 self.colstart = colstart
1122 self.colstart = colstart
1117 self.colend = colend
1123 self.colend = colend
1118
1124
1119 def __eq__(self, other):
1125 def __eq__(self, other):
1120 return self.line == other.line
1126 return self.line == other.line
1121
1127
1122 matches = {}
1128 matches = {}
1123 copies = {}
1129 copies = {}
1124 def grepbody(fn, rev, body):
1130 def grepbody(fn, rev, body):
1125 matches[rev].setdefault(fn, [])
1131 matches[rev].setdefault(fn, [])
1126 m = matches[rev][fn]
1132 m = matches[rev][fn]
1127 for lnum, cstart, cend, line in matchlines(body):
1133 for lnum, cstart, cend, line in matchlines(body):
1128 s = linestate(line, lnum, cstart, cend)
1134 s = linestate(line, lnum, cstart, cend)
1129 m.append(s)
1135 m.append(s)
1130
1136
1131 def difflinestates(a, b):
1137 def difflinestates(a, b):
1132 sm = difflib.SequenceMatcher(None, a, b)
1138 sm = difflib.SequenceMatcher(None, a, b)
1133 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1139 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1134 if tag == 'insert':
1140 if tag == 'insert':
1135 for i in xrange(blo, bhi):
1141 for i in xrange(blo, bhi):
1136 yield ('+', b[i])
1142 yield ('+', b[i])
1137 elif tag == 'delete':
1143 elif tag == 'delete':
1138 for i in xrange(alo, ahi):
1144 for i in xrange(alo, ahi):
1139 yield ('-', a[i])
1145 yield ('-', a[i])
1140 elif tag == 'replace':
1146 elif tag == 'replace':
1141 for i in xrange(alo, ahi):
1147 for i in xrange(alo, ahi):
1142 yield ('-', a[i])
1148 yield ('-', a[i])
1143 for i in xrange(blo, bhi):
1149 for i in xrange(blo, bhi):
1144 yield ('+', b[i])
1150 yield ('+', b[i])
1145
1151
1146 prev = {}
1152 prev = {}
1147 def display(fn, rev, states, prevstates):
1153 def display(fn, rev, states, prevstates):
1148 found = False
1154 found = False
1149 filerevmatches = {}
1155 filerevmatches = {}
1150 r = prev.get(fn, -1)
1156 r = prev.get(fn, -1)
1151 if opts['all']:
1157 if opts['all']:
1152 iter = difflinestates(states, prevstates)
1158 iter = difflinestates(states, prevstates)
1153 else:
1159 else:
1154 iter = [('', l) for l in prevstates]
1160 iter = [('', l) for l in prevstates]
1155 for change, l in iter:
1161 for change, l in iter:
1156 cols = [fn, str(r)]
1162 cols = [fn, str(r)]
1157 if opts['line_number']:
1163 if opts['line_number']:
1158 cols.append(str(l.linenum))
1164 cols.append(str(l.linenum))
1159 if opts['all']:
1165 if opts['all']:
1160 cols.append(change)
1166 cols.append(change)
1161 if opts['user']:
1167 if opts['user']:
1162 cols.append(ui.shortuser(get(r)[1]))
1168 cols.append(ui.shortuser(get(r)[1]))
1163 if opts['files_with_matches']:
1169 if opts['files_with_matches']:
1164 c = (fn, r)
1170 c = (fn, r)
1165 if c in filerevmatches:
1171 if c in filerevmatches:
1166 continue
1172 continue
1167 filerevmatches[c] = 1
1173 filerevmatches[c] = 1
1168 else:
1174 else:
1169 cols.append(l.line)
1175 cols.append(l.line)
1170 ui.write(sep.join(cols), eol)
1176 ui.write(sep.join(cols), eol)
1171 found = True
1177 found = True
1172 return found
1178 return found
1173
1179
1174 fstate = {}
1180 fstate = {}
1175 skip = {}
1181 skip = {}
1176 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1182 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1177 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1183 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1178 found = False
1184 found = False
1179 follow = opts.get('follow')
1185 follow = opts.get('follow')
1180 for st, rev, fns in changeiter:
1186 for st, rev, fns in changeiter:
1181 if st == 'window':
1187 if st == 'window':
1182 matches.clear()
1188 matches.clear()
1183 elif st == 'add':
1189 elif st == 'add':
1184 mf = repo.changectx(rev).manifest()
1190 mf = repo.changectx(rev).manifest()
1185 matches[rev] = {}
1191 matches[rev] = {}
1186 for fn in fns:
1192 for fn in fns:
1187 if fn in skip:
1193 if fn in skip:
1188 continue
1194 continue
1189 fstate.setdefault(fn, {})
1195 fstate.setdefault(fn, {})
1190 try:
1196 try:
1191 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1197 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1192 if follow:
1198 if follow:
1193 copied = getfile(fn).renamed(mf[fn])
1199 copied = getfile(fn).renamed(mf[fn])
1194 if copied:
1200 if copied:
1195 copies.setdefault(rev, {})[fn] = copied[0]
1201 copies.setdefault(rev, {})[fn] = copied[0]
1196 except KeyError:
1202 except KeyError:
1197 pass
1203 pass
1198 elif st == 'iter':
1204 elif st == 'iter':
1199 states = matches[rev].items()
1205 states = matches[rev].items()
1200 states.sort()
1206 states.sort()
1201 for fn, m in states:
1207 for fn, m in states:
1202 copy = copies.get(rev, {}).get(fn)
1208 copy = copies.get(rev, {}).get(fn)
1203 if fn in skip:
1209 if fn in skip:
1204 if copy:
1210 if copy:
1205 skip[copy] = True
1211 skip[copy] = True
1206 continue
1212 continue
1207 if fn in prev or fstate[fn]:
1213 if fn in prev or fstate[fn]:
1208 r = display(fn, rev, m, fstate[fn])
1214 r = display(fn, rev, m, fstate[fn])
1209 found = found or r
1215 found = found or r
1210 if r and not opts['all']:
1216 if r and not opts['all']:
1211 skip[fn] = True
1217 skip[fn] = True
1212 if copy:
1218 if copy:
1213 skip[copy] = True
1219 skip[copy] = True
1214 fstate[fn] = m
1220 fstate[fn] = m
1215 if copy:
1221 if copy:
1216 fstate[copy] = m
1222 fstate[copy] = m
1217 prev[fn] = rev
1223 prev[fn] = rev
1218
1224
1219 fstate = fstate.items()
1225 fstate = fstate.items()
1220 fstate.sort()
1226 fstate.sort()
1221 for fn, state in fstate:
1227 for fn, state in fstate:
1222 if fn in skip:
1228 if fn in skip:
1223 continue
1229 continue
1224 if fn not in copies.get(prev[fn], {}):
1230 if fn not in copies.get(prev[fn], {}):
1225 found = display(fn, rev, {}, state) or found
1231 found = display(fn, rev, {}, state) or found
1226 return (not found and 1) or 0
1232 return (not found and 1) or 0
1227
1233
1228 def heads(ui, repo, *branchrevs, **opts):
1234 def heads(ui, repo, *branchrevs, **opts):
1229 """show current repository heads or show branch heads
1235 """show current repository heads or show branch heads
1230
1236
1231 With no arguments, show all repository head changesets.
1237 With no arguments, show all repository head changesets.
1232
1238
1233 If branch or revisions names are given this will show the heads of
1239 If branch or revisions names are given this will show the heads of
1234 the specified branches or the branches those revisions are tagged
1240 the specified branches or the branches those revisions are tagged
1235 with.
1241 with.
1236
1242
1237 Repository "heads" are changesets that don't have child
1243 Repository "heads" are changesets that don't have child
1238 changesets. They are where development generally takes place and
1244 changesets. They are where development generally takes place and
1239 are the usual targets for update and merge operations.
1245 are the usual targets for update and merge operations.
1240
1246
1241 Branch heads are changesets that have a given branch tag, but have
1247 Branch heads are changesets that have a given branch tag, but have
1242 no child changesets with that tag. They are usually where
1248 no child changesets with that tag. They are usually where
1243 development on the given branch takes place.
1249 development on the given branch takes place.
1244 """
1250 """
1245 if opts['rev']:
1251 if opts['rev']:
1246 start = repo.lookup(opts['rev'])
1252 start = repo.lookup(opts['rev'])
1247 else:
1253 else:
1248 start = None
1254 start = None
1249 if not branchrevs:
1255 if not branchrevs:
1250 # Assume we're looking repo-wide heads if no revs were specified.
1256 # Assume we're looking repo-wide heads if no revs were specified.
1251 heads = repo.heads(start)
1257 heads = repo.heads(start)
1252 else:
1258 else:
1253 heads = []
1259 heads = []
1254 visitedset = util.set()
1260 visitedset = util.set()
1255 for branchrev in branchrevs:
1261 for branchrev in branchrevs:
1256 branch = repo.changectx(branchrev).branch()
1262 branch = repo.changectx(branchrev).branch()
1257 if branch in visitedset:
1263 if branch in visitedset:
1258 continue
1264 continue
1259 visitedset.add(branch)
1265 visitedset.add(branch)
1260 bheads = repo.branchheads(branch, start)
1266 bheads = repo.branchheads(branch, start)
1261 if not bheads:
1267 if not bheads:
1262 if branch != branchrev:
1268 if branch != branchrev:
1263 ui.warn(_("no changes on branch %s containing %s are "
1269 ui.warn(_("no changes on branch %s containing %s are "
1264 "reachable from %s\n")
1270 "reachable from %s\n")
1265 % (branch, branchrev, opts['rev']))
1271 % (branch, branchrev, opts['rev']))
1266 else:
1272 else:
1267 ui.warn(_("no changes on branch %s are reachable from %s\n")
1273 ui.warn(_("no changes on branch %s are reachable from %s\n")
1268 % (branch, opts['rev']))
1274 % (branch, opts['rev']))
1269 heads.extend(bheads)
1275 heads.extend(bheads)
1270 if not heads:
1276 if not heads:
1271 return 1
1277 return 1
1272 displayer = cmdutil.show_changeset(ui, repo, opts)
1278 displayer = cmdutil.show_changeset(ui, repo, opts)
1273 for n in heads:
1279 for n in heads:
1274 displayer.show(changenode=n)
1280 displayer.show(changenode=n)
1275
1281
1276 def help_(ui, name=None, with_version=False):
1282 def help_(ui, name=None, with_version=False):
1277 """show help for a command, extension, or list of commands
1283 """show help for a command, extension, or list of commands
1278
1284
1279 With no arguments, print a list of commands and short help.
1285 With no arguments, print a list of commands and short help.
1280
1286
1281 Given a command name, print help for that command.
1287 Given a command name, print help for that command.
1282
1288
1283 Given an extension name, print help for that extension, and the
1289 Given an extension name, print help for that extension, and the
1284 commands it provides."""
1290 commands it provides."""
1285 option_lists = []
1291 option_lists = []
1286
1292
1287 def addglobalopts(aliases):
1293 def addglobalopts(aliases):
1288 if ui.verbose:
1294 if ui.verbose:
1289 option_lists.append((_("global options:"), globalopts))
1295 option_lists.append((_("global options:"), globalopts))
1290 if name == 'shortlist':
1296 if name == 'shortlist':
1291 option_lists.append((_('use "hg help" for the full list '
1297 option_lists.append((_('use "hg help" for the full list '
1292 'of commands'), ()))
1298 'of commands'), ()))
1293 else:
1299 else:
1294 if name == 'shortlist':
1300 if name == 'shortlist':
1295 msg = _('use "hg help" for the full list of commands '
1301 msg = _('use "hg help" for the full list of commands '
1296 'or "hg -v" for details')
1302 'or "hg -v" for details')
1297 elif aliases:
1303 elif aliases:
1298 msg = _('use "hg -v help%s" to show aliases and '
1304 msg = _('use "hg -v help%s" to show aliases and '
1299 'global options') % (name and " " + name or "")
1305 'global options') % (name and " " + name or "")
1300 else:
1306 else:
1301 msg = _('use "hg -v help %s" to show global options') % name
1307 msg = _('use "hg -v help %s" to show global options') % name
1302 option_lists.append((msg, ()))
1308 option_lists.append((msg, ()))
1303
1309
1304 def helpcmd(name):
1310 def helpcmd(name):
1305 if with_version:
1311 if with_version:
1306 version_(ui)
1312 version_(ui)
1307 ui.write('\n')
1313 ui.write('\n')
1308 aliases, i = cmdutil.findcmd(ui, name)
1314 aliases, i = cmdutil.findcmd(ui, name)
1309 # synopsis
1315 # synopsis
1310 ui.write("%s\n\n" % i[2])
1316 ui.write("%s\n\n" % i[2])
1311
1317
1312 # description
1318 # description
1313 doc = i[0].__doc__
1319 doc = i[0].__doc__
1314 if not doc:
1320 if not doc:
1315 doc = _("(No help text available)")
1321 doc = _("(No help text available)")
1316 if ui.quiet:
1322 if ui.quiet:
1317 doc = doc.splitlines(0)[0]
1323 doc = doc.splitlines(0)[0]
1318 ui.write("%s\n" % doc.rstrip())
1324 ui.write("%s\n" % doc.rstrip())
1319
1325
1320 if not ui.quiet:
1326 if not ui.quiet:
1321 # aliases
1327 # aliases
1322 if len(aliases) > 1:
1328 if len(aliases) > 1:
1323 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1329 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1324
1330
1325 # options
1331 # options
1326 if i[1]:
1332 if i[1]:
1327 option_lists.append((_("options:\n"), i[1]))
1333 option_lists.append((_("options:\n"), i[1]))
1328
1334
1329 addglobalopts(False)
1335 addglobalopts(False)
1330
1336
1331 def helplist(select=None):
1337 def helplist(select=None):
1332 h = {}
1338 h = {}
1333 cmds = {}
1339 cmds = {}
1334 for c, e in table.items():
1340 for c, e in table.items():
1335 f = c.split("|", 1)[0]
1341 f = c.split("|", 1)[0]
1336 if select and not select(f):
1342 if select and not select(f):
1337 continue
1343 continue
1338 if name == "shortlist" and not f.startswith("^"):
1344 if name == "shortlist" and not f.startswith("^"):
1339 continue
1345 continue
1340 f = f.lstrip("^")
1346 f = f.lstrip("^")
1341 if not ui.debugflag and f.startswith("debug"):
1347 if not ui.debugflag and f.startswith("debug"):
1342 continue
1348 continue
1343 doc = e[0].__doc__
1349 doc = e[0].__doc__
1344 if not doc:
1350 if not doc:
1345 doc = _("(No help text available)")
1351 doc = _("(No help text available)")
1346 h[f] = doc.splitlines(0)[0].rstrip()
1352 h[f] = doc.splitlines(0)[0].rstrip()
1347 cmds[f] = c.lstrip("^")
1353 cmds[f] = c.lstrip("^")
1348
1354
1349 fns = h.keys()
1355 fns = h.keys()
1350 fns.sort()
1356 fns.sort()
1351 m = max(map(len, fns))
1357 m = max(map(len, fns))
1352 for f in fns:
1358 for f in fns:
1353 if ui.verbose:
1359 if ui.verbose:
1354 commands = cmds[f].replace("|",", ")
1360 commands = cmds[f].replace("|",", ")
1355 ui.write(" %s:\n %s\n"%(commands, h[f]))
1361 ui.write(" %s:\n %s\n"%(commands, h[f]))
1356 else:
1362 else:
1357 ui.write(' %-*s %s\n' % (m, f, h[f]))
1363 ui.write(' %-*s %s\n' % (m, f, h[f]))
1358
1364
1359 if not ui.quiet:
1365 if not ui.quiet:
1360 addglobalopts(True)
1366 addglobalopts(True)
1361
1367
1362 def helptopic(name):
1368 def helptopic(name):
1363 v = None
1369 v = None
1364 for i in help.helptable:
1370 for i in help.helptable:
1365 l = i.split('|')
1371 l = i.split('|')
1366 if name in l:
1372 if name in l:
1367 v = i
1373 v = i
1368 header = l[-1]
1374 header = l[-1]
1369 if not v:
1375 if not v:
1370 raise cmdutil.UnknownCommand(name)
1376 raise cmdutil.UnknownCommand(name)
1371
1377
1372 # description
1378 # description
1373 doc = help.helptable[v]
1379 doc = help.helptable[v]
1374 if not doc:
1380 if not doc:
1375 doc = _("(No help text available)")
1381 doc = _("(No help text available)")
1376 if callable(doc):
1382 if callable(doc):
1377 doc = doc()
1383 doc = doc()
1378
1384
1379 ui.write("%s\n" % header)
1385 ui.write("%s\n" % header)
1380 ui.write("%s\n" % doc.rstrip())
1386 ui.write("%s\n" % doc.rstrip())
1381
1387
1382 def helpext(name):
1388 def helpext(name):
1383 try:
1389 try:
1384 mod = extensions.find(name)
1390 mod = extensions.find(name)
1385 except KeyError:
1391 except KeyError:
1386 raise cmdutil.UnknownCommand(name)
1392 raise cmdutil.UnknownCommand(name)
1387
1393
1388 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1394 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1389 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1395 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1390 for d in doc[1:]:
1396 for d in doc[1:]:
1391 ui.write(d, '\n')
1397 ui.write(d, '\n')
1392
1398
1393 ui.status('\n')
1399 ui.status('\n')
1394
1400
1395 try:
1401 try:
1396 ct = mod.cmdtable
1402 ct = mod.cmdtable
1397 except AttributeError:
1403 except AttributeError:
1398 ct = None
1404 ct = None
1399 if not ct:
1405 if not ct:
1400 ui.status(_('no commands defined\n'))
1406 ui.status(_('no commands defined\n'))
1401 return
1407 return
1402
1408
1403 ui.status(_('list of commands:\n\n'))
1409 ui.status(_('list of commands:\n\n'))
1404 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1410 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1405 helplist(modcmds.has_key)
1411 helplist(modcmds.has_key)
1406
1412
1407 if name and name != 'shortlist':
1413 if name and name != 'shortlist':
1408 i = None
1414 i = None
1409 for f in (helpcmd, helptopic, helpext):
1415 for f in (helpcmd, helptopic, helpext):
1410 try:
1416 try:
1411 f(name)
1417 f(name)
1412 i = None
1418 i = None
1413 break
1419 break
1414 except cmdutil.UnknownCommand, inst:
1420 except cmdutil.UnknownCommand, inst:
1415 i = inst
1421 i = inst
1416 if i:
1422 if i:
1417 raise i
1423 raise i
1418
1424
1419 else:
1425 else:
1420 # program name
1426 # program name
1421 if ui.verbose or with_version:
1427 if ui.verbose or with_version:
1422 version_(ui)
1428 version_(ui)
1423 else:
1429 else:
1424 ui.status(_("Mercurial Distributed SCM\n"))
1430 ui.status(_("Mercurial Distributed SCM\n"))
1425 ui.status('\n')
1431 ui.status('\n')
1426
1432
1427 # list of commands
1433 # list of commands
1428 if name == "shortlist":
1434 if name == "shortlist":
1429 ui.status(_('basic commands:\n\n'))
1435 ui.status(_('basic commands:\n\n'))
1430 else:
1436 else:
1431 ui.status(_('list of commands:\n\n'))
1437 ui.status(_('list of commands:\n\n'))
1432
1438
1433 helplist()
1439 helplist()
1434
1440
1435 # list all option lists
1441 # list all option lists
1436 opt_output = []
1442 opt_output = []
1437 for title, options in option_lists:
1443 for title, options in option_lists:
1438 opt_output.append(("\n%s" % title, None))
1444 opt_output.append(("\n%s" % title, None))
1439 for shortopt, longopt, default, desc in options:
1445 for shortopt, longopt, default, desc in options:
1440 if "DEPRECATED" in desc and not ui.verbose: continue
1446 if "DEPRECATED" in desc and not ui.verbose: continue
1441 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1447 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1442 longopt and " --%s" % longopt),
1448 longopt and " --%s" % longopt),
1443 "%s%s" % (desc,
1449 "%s%s" % (desc,
1444 default
1450 default
1445 and _(" (default: %s)") % default
1451 and _(" (default: %s)") % default
1446 or "")))
1452 or "")))
1447
1453
1448 if opt_output:
1454 if opt_output:
1449 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1455 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1450 for first, second in opt_output:
1456 for first, second in opt_output:
1451 if second:
1457 if second:
1452 ui.write(" %-*s %s\n" % (opts_len, first, second))
1458 ui.write(" %-*s %s\n" % (opts_len, first, second))
1453 else:
1459 else:
1454 ui.write("%s\n" % first)
1460 ui.write("%s\n" % first)
1455
1461
1456 def identify(ui, repo, source=None,
1462 def identify(ui, repo, source=None,
1457 rev=None, num=None, id=None, branch=None, tags=None):
1463 rev=None, num=None, id=None, branch=None, tags=None):
1458 """identify the working copy or specified revision
1464 """identify the working copy or specified revision
1459
1465
1460 With no revision, print a summary of the current state of the repo.
1466 With no revision, print a summary of the current state of the repo.
1461
1467
1462 With a path, do a lookup in another repository.
1468 With a path, do a lookup in another repository.
1463
1469
1464 This summary identifies the repository state using one or two parent
1470 This summary identifies the repository state using one or two parent
1465 hash identifiers, followed by a "+" if there are uncommitted changes
1471 hash identifiers, followed by a "+" if there are uncommitted changes
1466 in the working directory, a list of tags for this revision and a branch
1472 in the working directory, a list of tags for this revision and a branch
1467 name for non-default branches.
1473 name for non-default branches.
1468 """
1474 """
1469
1475
1470 hexfunc = ui.debugflag and hex or short
1476 hexfunc = ui.debugflag and hex or short
1471 default = not (num or id or branch or tags)
1477 default = not (num or id or branch or tags)
1472 output = []
1478 output = []
1473
1479
1474 if source:
1480 if source:
1475 source, revs = cmdutil.parseurl(ui.expandpath(source), [])
1481 source, revs = cmdutil.parseurl(ui.expandpath(source), [])
1476 srepo = hg.repository(ui, source)
1482 srepo = hg.repository(ui, source)
1477 if not rev and revs:
1483 if not rev and revs:
1478 rev = revs[0]
1484 rev = revs[0]
1479 if not rev:
1485 if not rev:
1480 rev = "tip"
1486 rev = "tip"
1481 if num or branch or tags:
1487 if num or branch or tags:
1482 raise util.Abort(
1488 raise util.Abort(
1483 "can't query remote revision number, branch, or tags")
1489 "can't query remote revision number, branch, or tags")
1484 output = [hexfunc(srepo.lookup(rev))]
1490 output = [hexfunc(srepo.lookup(rev))]
1485 elif not rev:
1491 elif not rev:
1486 ctx = repo.workingctx()
1492 ctx = repo.workingctx()
1487 parents = ctx.parents()
1493 parents = ctx.parents()
1488 changed = False
1494 changed = False
1489 if default or id or num:
1495 if default or id or num:
1490 changed = ctx.files() + ctx.deleted()
1496 changed = ctx.files() + ctx.deleted()
1491 if default or id:
1497 if default or id:
1492 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1498 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1493 (changed) and "+" or "")]
1499 (changed) and "+" or "")]
1494 if num:
1500 if num:
1495 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1501 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1496 (changed) and "+" or ""))
1502 (changed) and "+" or ""))
1497 else:
1503 else:
1498 ctx = repo.changectx(rev)
1504 ctx = repo.changectx(rev)
1499 if default or id:
1505 if default or id:
1500 output = [hexfunc(ctx.node())]
1506 output = [hexfunc(ctx.node())]
1501 if num:
1507 if num:
1502 output.append(str(ctx.rev()))
1508 output.append(str(ctx.rev()))
1503
1509
1504 if not source and default and not ui.quiet:
1510 if not source and default and not ui.quiet:
1505 b = util.tolocal(ctx.branch())
1511 b = util.tolocal(ctx.branch())
1506 if b != 'default':
1512 if b != 'default':
1507 output.append("(%s)" % b)
1513 output.append("(%s)" % b)
1508
1514
1509 # multiple tags for a single parent separated by '/'
1515 # multiple tags for a single parent separated by '/'
1510 t = "/".join(ctx.tags())
1516 t = "/".join(ctx.tags())
1511 if t:
1517 if t:
1512 output.append(t)
1518 output.append(t)
1513
1519
1514 if branch:
1520 if branch:
1515 output.append(util.tolocal(ctx.branch()))
1521 output.append(util.tolocal(ctx.branch()))
1516
1522
1517 if tags:
1523 if tags:
1518 output.extend(ctx.tags())
1524 output.extend(ctx.tags())
1519
1525
1520 ui.write("%s\n" % ' '.join(output))
1526 ui.write("%s\n" % ' '.join(output))
1521
1527
1522 def import_(ui, repo, patch1, *patches, **opts):
1528 def import_(ui, repo, patch1, *patches, **opts):
1523 """import an ordered set of patches
1529 """import an ordered set of patches
1524
1530
1525 Import a list of patches and commit them individually.
1531 Import a list of patches and commit them individually.
1526
1532
1527 If there are outstanding changes in the working directory, import
1533 If there are outstanding changes in the working directory, import
1528 will abort unless given the -f flag.
1534 will abort unless given the -f flag.
1529
1535
1530 You can import a patch straight from a mail message. Even patches
1536 You can import a patch straight from a mail message. Even patches
1531 as attachments work (body part must be type text/plain or
1537 as attachments work (body part must be type text/plain or
1532 text/x-patch to be used). From and Subject headers of email
1538 text/x-patch to be used). From and Subject headers of email
1533 message are used as default committer and commit message. All
1539 message are used as default committer and commit message. All
1534 text/plain body parts before first diff are added to commit
1540 text/plain body parts before first diff are added to commit
1535 message.
1541 message.
1536
1542
1537 If the imported patch was generated by hg export, user and description
1543 If the imported patch was generated by hg export, user and description
1538 from patch override values from message headers and body. Values
1544 from patch override values from message headers and body. Values
1539 given on command line with -m and -u override these.
1545 given on command line with -m and -u override these.
1540
1546
1541 If --exact is specified, import will set the working directory
1547 If --exact is specified, import will set the working directory
1542 to the parent of each patch before applying it, and will abort
1548 to the parent of each patch before applying it, and will abort
1543 if the resulting changeset has a different ID than the one
1549 if the resulting changeset has a different ID than the one
1544 recorded in the patch. This may happen due to character set
1550 recorded in the patch. This may happen due to character set
1545 problems or other deficiencies in the text patch format.
1551 problems or other deficiencies in the text patch format.
1546
1552
1547 To read a patch from standard input, use patch name "-".
1553 To read a patch from standard input, use patch name "-".
1548 """
1554 """
1549 patches = (patch1,) + patches
1555 patches = (patch1,) + patches
1550
1556
1551 if opts.get('exact') or not opts['force']:
1557 if opts.get('exact') or not opts['force']:
1552 cmdutil.bail_if_changed(repo)
1558 cmdutil.bail_if_changed(repo)
1553
1559
1554 d = opts["base"]
1560 d = opts["base"]
1555 strip = opts["strip"]
1561 strip = opts["strip"]
1556
1562
1557 wlock = repo.wlock()
1563 wlock = repo.wlock()
1558 lock = repo.lock()
1564 lock = repo.lock()
1559
1565
1560 for p in patches:
1566 for p in patches:
1561 pf = os.path.join(d, p)
1567 pf = os.path.join(d, p)
1562
1568
1563 if pf == '-':
1569 if pf == '-':
1564 ui.status(_("applying patch from stdin\n"))
1570 ui.status(_("applying patch from stdin\n"))
1565 tmpname, message, user, date, branch, nodeid, p1, p2 = patch.extract(ui, sys.stdin)
1571 tmpname, message, user, date, branch, nodeid, p1, p2 = patch.extract(ui, sys.stdin)
1566 else:
1572 else:
1567 ui.status(_("applying %s\n") % p)
1573 ui.status(_("applying %s\n") % p)
1568 tmpname, message, user, date, branch, nodeid, p1, p2 = patch.extract(ui, file(pf, 'rb'))
1574 tmpname, message, user, date, branch, nodeid, p1, p2 = patch.extract(ui, file(pf, 'rb'))
1569
1575
1570 if tmpname is None:
1576 if tmpname is None:
1571 raise util.Abort(_('no diffs found'))
1577 raise util.Abort(_('no diffs found'))
1572
1578
1573 try:
1579 try:
1574 cmdline_message = cmdutil.logmessage(opts)
1580 cmdline_message = cmdutil.logmessage(opts)
1575 if cmdline_message:
1581 if cmdline_message:
1576 # pickup the cmdline msg
1582 # pickup the cmdline msg
1577 message = cmdline_message
1583 message = cmdline_message
1578 elif message:
1584 elif message:
1579 # pickup the patch msg
1585 # pickup the patch msg
1580 message = message.strip()
1586 message = message.strip()
1581 else:
1587 else:
1582 # launch the editor
1588 # launch the editor
1583 message = None
1589 message = None
1584 ui.debug(_('message:\n%s\n') % message)
1590 ui.debug(_('message:\n%s\n') % message)
1585
1591
1586 wp = repo.workingctx().parents()
1592 wp = repo.workingctx().parents()
1587 if opts.get('exact'):
1593 if opts.get('exact'):
1588 if not nodeid or not p1:
1594 if not nodeid or not p1:
1589 raise util.Abort(_('not a mercurial patch'))
1595 raise util.Abort(_('not a mercurial patch'))
1590 p1 = repo.lookup(p1)
1596 p1 = repo.lookup(p1)
1591 p2 = repo.lookup(p2 or hex(nullid))
1597 p2 = repo.lookup(p2 or hex(nullid))
1592
1598
1593 if p1 != wp[0].node():
1599 if p1 != wp[0].node():
1594 hg.clean(repo, p1, wlock=wlock)
1600 hg.clean(repo, p1, wlock=wlock)
1595 repo.dirstate.setparents(p1, p2)
1601 repo.dirstate.setparents(p1, p2)
1596 elif p2:
1602 elif p2:
1597 try:
1603 try:
1598 p1 = repo.lookup(p1)
1604 p1 = repo.lookup(p1)
1599 p2 = repo.lookup(p2)
1605 p2 = repo.lookup(p2)
1600 if p1 == wp[0].node():
1606 if p1 == wp[0].node():
1601 repo.dirstate.setparents(p1, p2)
1607 repo.dirstate.setparents(p1, p2)
1602 except hg.RepoError:
1608 except hg.RepoError:
1603 pass
1609 pass
1604 if opts.get('exact') or opts.get('import_branch'):
1610 if opts.get('exact') or opts.get('import_branch'):
1605 repo.dirstate.setbranch(branch or 'default')
1611 repo.dirstate.setbranch(branch or 'default')
1606
1612
1607 files = {}
1613 files = {}
1608 try:
1614 try:
1609 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1615 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1610 files=files)
1616 files=files)
1611 finally:
1617 finally:
1612 files = patch.updatedir(ui, repo, files, wlock=wlock)
1618 files = patch.updatedir(ui, repo, files, wlock=wlock)
1613 n = repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1619 n = repo.commit(files, message, user, date, wlock=wlock, lock=lock)
1614 if opts.get('exact'):
1620 if opts.get('exact'):
1615 if hex(n) != nodeid:
1621 if hex(n) != nodeid:
1616 repo.rollback(wlock=wlock, lock=lock)
1622 repo.rollback(wlock=wlock, lock=lock)
1617 raise util.Abort(_('patch is damaged or loses information'))
1623 raise util.Abort(_('patch is damaged or loses information'))
1618 finally:
1624 finally:
1619 os.unlink(tmpname)
1625 os.unlink(tmpname)
1620
1626
1621 def incoming(ui, repo, source="default", **opts):
1627 def incoming(ui, repo, source="default", **opts):
1622 """show new changesets found in source
1628 """show new changesets found in source
1623
1629
1624 Show new changesets found in the specified path/URL or the default
1630 Show new changesets found in the specified path/URL or the default
1625 pull location. These are the changesets that would be pulled if a pull
1631 pull location. These are the changesets that would be pulled if a pull
1626 was requested.
1632 was requested.
1627
1633
1628 For remote repository, using --bundle avoids downloading the changesets
1634 For remote repository, using --bundle avoids downloading the changesets
1629 twice if the incoming is followed by a pull.
1635 twice if the incoming is followed by a pull.
1630
1636
1631 See pull for valid source format details.
1637 See pull for valid source format details.
1632 """
1638 """
1633 source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
1639 source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
1634 cmdutil.setremoteconfig(ui, opts)
1640 cmdutil.setremoteconfig(ui, opts)
1635
1641
1636 other = hg.repository(ui, source)
1642 other = hg.repository(ui, source)
1637 ui.status(_('comparing with %s\n') % source)
1643 ui.status(_('comparing with %s\n') % source)
1638 if revs:
1644 if revs:
1639 if 'lookup' in other.capabilities:
1645 if 'lookup' in other.capabilities:
1640 revs = [other.lookup(rev) for rev in revs]
1646 revs = [other.lookup(rev) for rev in revs]
1641 else:
1647 else:
1642 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1648 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
1643 raise util.Abort(error)
1649 raise util.Abort(error)
1644 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1650 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1645 if not incoming:
1651 if not incoming:
1646 try:
1652 try:
1647 os.unlink(opts["bundle"])
1653 os.unlink(opts["bundle"])
1648 except:
1654 except:
1649 pass
1655 pass
1650 ui.status(_("no changes found\n"))
1656 ui.status(_("no changes found\n"))
1651 return 1
1657 return 1
1652
1658
1653 cleanup = None
1659 cleanup = None
1654 try:
1660 try:
1655 fname = opts["bundle"]
1661 fname = opts["bundle"]
1656 if fname or not other.local():
1662 if fname or not other.local():
1657 # create a bundle (uncompressed if other repo is not local)
1663 # create a bundle (uncompressed if other repo is not local)
1658 if revs is None:
1664 if revs is None:
1659 cg = other.changegroup(incoming, "incoming")
1665 cg = other.changegroup(incoming, "incoming")
1660 else:
1666 else:
1661 if 'changegroupsubset' not in other.capabilities:
1667 if 'changegroupsubset' not in other.capabilities:
1662 raise util.Abort(_("Partial incoming cannot be done because other repository doesn't support changegroupsubset."))
1668 raise util.Abort(_("Partial incoming cannot be done because other repository doesn't support changegroupsubset."))
1663 cg = other.changegroupsubset(incoming, revs, 'incoming')
1669 cg = other.changegroupsubset(incoming, revs, 'incoming')
1664 bundletype = other.local() and "HG10BZ" or "HG10UN"
1670 bundletype = other.local() and "HG10BZ" or "HG10UN"
1665 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1671 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1666 # keep written bundle?
1672 # keep written bundle?
1667 if opts["bundle"]:
1673 if opts["bundle"]:
1668 cleanup = None
1674 cleanup = None
1669 if not other.local():
1675 if not other.local():
1670 # use the created uncompressed bundlerepo
1676 # use the created uncompressed bundlerepo
1671 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1677 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1672
1678
1673 o = other.changelog.nodesbetween(incoming, revs)[0]
1679 o = other.changelog.nodesbetween(incoming, revs)[0]
1674 if opts['newest_first']:
1680 if opts['newest_first']:
1675 o.reverse()
1681 o.reverse()
1676 displayer = cmdutil.show_changeset(ui, other, opts)
1682 displayer = cmdutil.show_changeset(ui, other, opts)
1677 for n in o:
1683 for n in o:
1678 parents = [p for p in other.changelog.parents(n) if p != nullid]
1684 parents = [p for p in other.changelog.parents(n) if p != nullid]
1679 if opts['no_merges'] and len(parents) == 2:
1685 if opts['no_merges'] and len(parents) == 2:
1680 continue
1686 continue
1681 displayer.show(changenode=n)
1687 displayer.show(changenode=n)
1682 finally:
1688 finally:
1683 if hasattr(other, 'close'):
1689 if hasattr(other, 'close'):
1684 other.close()
1690 other.close()
1685 if cleanup:
1691 if cleanup:
1686 os.unlink(cleanup)
1692 os.unlink(cleanup)
1687
1693
1688 def init(ui, dest=".", **opts):
1694 def init(ui, dest=".", **opts):
1689 """create a new repository in the given directory
1695 """create a new repository in the given directory
1690
1696
1691 Initialize a new repository in the given directory. If the given
1697 Initialize a new repository in the given directory. If the given
1692 directory does not exist, it is created.
1698 directory does not exist, it is created.
1693
1699
1694 If no directory is given, the current directory is used.
1700 If no directory is given, the current directory is used.
1695
1701
1696 It is possible to specify an ssh:// URL as the destination.
1702 It is possible to specify an ssh:// URL as the destination.
1697 Look at the help text for the pull command for important details
1703 Look at the help text for the pull command for important details
1698 about ssh:// URLs.
1704 about ssh:// URLs.
1699 """
1705 """
1700 cmdutil.setremoteconfig(ui, opts)
1706 cmdutil.setremoteconfig(ui, opts)
1701 hg.repository(ui, dest, create=1)
1707 hg.repository(ui, dest, create=1)
1702
1708
1703 def locate(ui, repo, *pats, **opts):
1709 def locate(ui, repo, *pats, **opts):
1704 """locate files matching specific patterns
1710 """locate files matching specific patterns
1705
1711
1706 Print all files under Mercurial control whose names match the
1712 Print all files under Mercurial control whose names match the
1707 given patterns.
1713 given patterns.
1708
1714
1709 This command searches the entire repository by default. To search
1715 This command searches the entire repository by default. To search
1710 just the current directory and its subdirectories, use
1716 just the current directory and its subdirectories, use
1711 "--include .".
1717 "--include .".
1712
1718
1713 If no patterns are given to match, this command prints all file
1719 If no patterns are given to match, this command prints all file
1714 names.
1720 names.
1715
1721
1716 If you want to feed the output of this command into the "xargs"
1722 If you want to feed the output of this command into the "xargs"
1717 command, use the "-0" option to both this command and "xargs".
1723 command, use the "-0" option to both this command and "xargs".
1718 This will avoid the problem of "xargs" treating single filenames
1724 This will avoid the problem of "xargs" treating single filenames
1719 that contain white space as multiple filenames.
1725 that contain white space as multiple filenames.
1720 """
1726 """
1721 end = opts['print0'] and '\0' or '\n'
1727 end = opts['print0'] and '\0' or '\n'
1722 rev = opts['rev']
1728 rev = opts['rev']
1723 if rev:
1729 if rev:
1724 node = repo.lookup(rev)
1730 node = repo.lookup(rev)
1725 else:
1731 else:
1726 node = None
1732 node = None
1727
1733
1728 ret = 1
1734 ret = 1
1729 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1735 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1730 badmatch=util.always,
1736 badmatch=util.always,
1731 default='relglob'):
1737 default='relglob'):
1732 if src == 'b':
1738 if src == 'b':
1733 continue
1739 continue
1734 if not node and repo.dirstate.state(abs) == '?':
1740 if not node and repo.dirstate.state(abs) == '?':
1735 continue
1741 continue
1736 if opts['fullpath']:
1742 if opts['fullpath']:
1737 ui.write(os.path.join(repo.root, abs), end)
1743 ui.write(os.path.join(repo.root, abs), end)
1738 else:
1744 else:
1739 ui.write(((pats and rel) or abs), end)
1745 ui.write(((pats and rel) or abs), end)
1740 ret = 0
1746 ret = 0
1741
1747
1742 return ret
1748 return ret
1743
1749
1744 def log(ui, repo, *pats, **opts):
1750 def log(ui, repo, *pats, **opts):
1745 """show revision history of entire repository or files
1751 """show revision history of entire repository or files
1746
1752
1747 Print the revision history of the specified files or the entire
1753 Print the revision history of the specified files or the entire
1748 project.
1754 project.
1749
1755
1750 File history is shown without following rename or copy history of
1756 File history is shown without following rename or copy history of
1751 files. Use -f/--follow with a file name to follow history across
1757 files. Use -f/--follow with a file name to follow history across
1752 renames and copies. --follow without a file name will only show
1758 renames and copies. --follow without a file name will only show
1753 ancestors or descendants of the starting revision. --follow-first
1759 ancestors or descendants of the starting revision. --follow-first
1754 only follows the first parent of merge revisions.
1760 only follows the first parent of merge revisions.
1755
1761
1756 If no revision range is specified, the default is tip:0 unless
1762 If no revision range is specified, the default is tip:0 unless
1757 --follow is set, in which case the working directory parent is
1763 --follow is set, in which case the working directory parent is
1758 used as the starting revision.
1764 used as the starting revision.
1759
1765
1760 By default this command outputs: changeset id and hash, tags,
1766 By default this command outputs: changeset id and hash, tags,
1761 non-trivial parents, user, date and time, and a summary for each
1767 non-trivial parents, user, date and time, and a summary for each
1762 commit. When the -v/--verbose switch is used, the list of changed
1768 commit. When the -v/--verbose switch is used, the list of changed
1763 files and full commit message is shown.
1769 files and full commit message is shown.
1764
1770
1765 NOTE: log -p may generate unexpected diff output for merge
1771 NOTE: log -p may generate unexpected diff output for merge
1766 changesets, as it will compare the merge changeset against its
1772 changesets, as it will compare the merge changeset against its
1767 first parent only. Also, the files: list will only reflect files
1773 first parent only. Also, the files: list will only reflect files
1768 that are different from BOTH parents.
1774 that are different from BOTH parents.
1769
1775
1770 """
1776 """
1771
1777
1772 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1778 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1773 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1779 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1774
1780
1775 if opts['limit']:
1781 if opts['limit']:
1776 try:
1782 try:
1777 limit = int(opts['limit'])
1783 limit = int(opts['limit'])
1778 except ValueError:
1784 except ValueError:
1779 raise util.Abort(_('limit must be a positive integer'))
1785 raise util.Abort(_('limit must be a positive integer'))
1780 if limit <= 0: raise util.Abort(_('limit must be positive'))
1786 if limit <= 0: raise util.Abort(_('limit must be positive'))
1781 else:
1787 else:
1782 limit = sys.maxint
1788 limit = sys.maxint
1783 count = 0
1789 count = 0
1784
1790
1785 if opts['copies'] and opts['rev']:
1791 if opts['copies'] and opts['rev']:
1786 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1792 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1787 else:
1793 else:
1788 endrev = repo.changelog.count()
1794 endrev = repo.changelog.count()
1789 rcache = {}
1795 rcache = {}
1790 ncache = {}
1796 ncache = {}
1791 dcache = []
1797 dcache = []
1792 def getrenamed(fn, rev, man):
1798 def getrenamed(fn, rev, man):
1793 '''looks up all renames for a file (up to endrev) the first
1799 '''looks up all renames for a file (up to endrev) the first
1794 time the file is given. It indexes on the changerev and only
1800 time the file is given. It indexes on the changerev and only
1795 parses the manifest if linkrev != changerev.
1801 parses the manifest if linkrev != changerev.
1796 Returns rename info for fn at changerev rev.'''
1802 Returns rename info for fn at changerev rev.'''
1797 if fn not in rcache:
1803 if fn not in rcache:
1798 rcache[fn] = {}
1804 rcache[fn] = {}
1799 ncache[fn] = {}
1805 ncache[fn] = {}
1800 fl = repo.file(fn)
1806 fl = repo.file(fn)
1801 for i in xrange(fl.count()):
1807 for i in xrange(fl.count()):
1802 node = fl.node(i)
1808 node = fl.node(i)
1803 lr = fl.linkrev(node)
1809 lr = fl.linkrev(node)
1804 renamed = fl.renamed(node)
1810 renamed = fl.renamed(node)
1805 rcache[fn][lr] = renamed
1811 rcache[fn][lr] = renamed
1806 if renamed:
1812 if renamed:
1807 ncache[fn][node] = renamed
1813 ncache[fn][node] = renamed
1808 if lr >= endrev:
1814 if lr >= endrev:
1809 break
1815 break
1810 if rev in rcache[fn]:
1816 if rev in rcache[fn]:
1811 return rcache[fn][rev]
1817 return rcache[fn][rev]
1812 mr = repo.manifest.rev(man)
1818 mr = repo.manifest.rev(man)
1813 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1819 if repo.manifest.parentrevs(mr) != (mr - 1, nullrev):
1814 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1820 return ncache[fn].get(repo.manifest.find(man, fn)[0])
1815 if not dcache or dcache[0] != man:
1821 if not dcache or dcache[0] != man:
1816 dcache[:] = [man, repo.manifest.readdelta(man)]
1822 dcache[:] = [man, repo.manifest.readdelta(man)]
1817 if fn in dcache[1]:
1823 if fn in dcache[1]:
1818 return ncache[fn].get(dcache[1][fn])
1824 return ncache[fn].get(dcache[1][fn])
1819 return None
1825 return None
1820
1826
1821 df = False
1827 df = False
1822 if opts["date"]:
1828 if opts["date"]:
1823 df = util.matchdate(opts["date"])
1829 df = util.matchdate(opts["date"])
1824
1830
1825 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1831 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1826 for st, rev, fns in changeiter:
1832 for st, rev, fns in changeiter:
1827 if st == 'add':
1833 if st == 'add':
1828 changenode = repo.changelog.node(rev)
1834 changenode = repo.changelog.node(rev)
1829 parents = [p for p in repo.changelog.parentrevs(rev)
1835 parents = [p for p in repo.changelog.parentrevs(rev)
1830 if p != nullrev]
1836 if p != nullrev]
1831 if opts['no_merges'] and len(parents) == 2:
1837 if opts['no_merges'] and len(parents) == 2:
1832 continue
1838 continue
1833 if opts['only_merges'] and len(parents) != 2:
1839 if opts['only_merges'] and len(parents) != 2:
1834 continue
1840 continue
1835
1841
1836 if df:
1842 if df:
1837 changes = get(rev)
1843 changes = get(rev)
1838 if not df(changes[2][0]):
1844 if not df(changes[2][0]):
1839 continue
1845 continue
1840
1846
1841 if opts['keyword']:
1847 if opts['keyword']:
1842 changes = get(rev)
1848 changes = get(rev)
1843 miss = 0
1849 miss = 0
1844 for k in [kw.lower() for kw in opts['keyword']]:
1850 for k in [kw.lower() for kw in opts['keyword']]:
1845 if not (k in changes[1].lower() or
1851 if not (k in changes[1].lower() or
1846 k in changes[4].lower() or
1852 k in changes[4].lower() or
1847 k in " ".join(changes[3]).lower()):
1853 k in " ".join(changes[3]).lower()):
1848 miss = 1
1854 miss = 1
1849 break
1855 break
1850 if miss:
1856 if miss:
1851 continue
1857 continue
1852
1858
1853 copies = []
1859 copies = []
1854 if opts.get('copies') and rev:
1860 if opts.get('copies') and rev:
1855 mf = get(rev)[0]
1861 mf = get(rev)[0]
1856 for fn in get(rev)[3]:
1862 for fn in get(rev)[3]:
1857 rename = getrenamed(fn, rev, mf)
1863 rename = getrenamed(fn, rev, mf)
1858 if rename:
1864 if rename:
1859 copies.append((fn, rename[0]))
1865 copies.append((fn, rename[0]))
1860 displayer.show(rev, changenode, copies=copies)
1866 displayer.show(rev, changenode, copies=copies)
1861 elif st == 'iter':
1867 elif st == 'iter':
1862 if count == limit: break
1868 if count == limit: break
1863 if displayer.flush(rev):
1869 if displayer.flush(rev):
1864 count += 1
1870 count += 1
1865
1871
1866 def manifest(ui, repo, rev=None):
1872 def manifest(ui, repo, rev=None):
1867 """output the current or given revision of the project manifest
1873 """output the current or given revision of the project manifest
1868
1874
1869 Print a list of version controlled files for the given revision.
1875 Print a list of version controlled files for the given revision.
1870 If no revision is given, the parent of the working directory is used,
1876 If no revision is given, the parent of the working directory is used,
1871 or tip if no revision is checked out.
1877 or tip if no revision is checked out.
1872
1878
1873 The manifest is the list of files being version controlled. If no revision
1879 The manifest is the list of files being version controlled. If no revision
1874 is given then the first parent of the working directory is used.
1880 is given then the first parent of the working directory is used.
1875
1881
1876 With -v flag, print file permissions. With --debug flag, print
1882 With -v flag, print file permissions. With --debug flag, print
1877 file revision hashes.
1883 file revision hashes.
1878 """
1884 """
1879
1885
1880 m = repo.changectx(rev).manifest()
1886 m = repo.changectx(rev).manifest()
1881 files = m.keys()
1887 files = m.keys()
1882 files.sort()
1888 files.sort()
1883
1889
1884 for f in files:
1890 for f in files:
1885 if ui.debugflag:
1891 if ui.debugflag:
1886 ui.write("%40s " % hex(m[f]))
1892 ui.write("%40s " % hex(m[f]))
1887 if ui.verbose:
1893 if ui.verbose:
1888 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1894 ui.write("%3s " % (m.execf(f) and "755" or "644"))
1889 ui.write("%s\n" % f)
1895 ui.write("%s\n" % f)
1890
1896
1891 def merge(ui, repo, node=None, force=None, rev=None):
1897 def merge(ui, repo, node=None, force=None, rev=None):
1892 """merge working directory with another revision
1898 """merge working directory with another revision
1893
1899
1894 Merge the contents of the current working directory and the
1900 Merge the contents of the current working directory and the
1895 requested revision. Files that changed between either parent are
1901 requested revision. Files that changed between either parent are
1896 marked as changed for the next commit and a commit must be
1902 marked as changed for the next commit and a commit must be
1897 performed before any further updates are allowed.
1903 performed before any further updates are allowed.
1898
1904
1899 If no revision is specified, the working directory's parent is a
1905 If no revision is specified, the working directory's parent is a
1900 head revision, and the repository contains exactly one other head,
1906 head revision, and the repository contains exactly one other head,
1901 the other head is merged with by default. Otherwise, an explicit
1907 the other head is merged with by default. Otherwise, an explicit
1902 revision to merge with must be provided.
1908 revision to merge with must be provided.
1903 """
1909 """
1904
1910
1905 if rev and node:
1911 if rev and node:
1906 raise util.Abort(_("please specify just one revision"))
1912 raise util.Abort(_("please specify just one revision"))
1907
1913
1908 if not node:
1914 if not node:
1909 node = rev
1915 node = rev
1910
1916
1911 if not node:
1917 if not node:
1912 heads = repo.heads()
1918 heads = repo.heads()
1913 if len(heads) > 2:
1919 if len(heads) > 2:
1914 raise util.Abort(_('repo has %d heads - '
1920 raise util.Abort(_('repo has %d heads - '
1915 'please merge with an explicit rev') %
1921 'please merge with an explicit rev') %
1916 len(heads))
1922 len(heads))
1917 if len(heads) == 1:
1923 if len(heads) == 1:
1918 raise util.Abort(_('there is nothing to merge - '
1924 raise util.Abort(_('there is nothing to merge - '
1919 'use "hg update" instead'))
1925 'use "hg update" instead'))
1920 parent = repo.dirstate.parents()[0]
1926 parent = repo.dirstate.parents()[0]
1921 if parent not in heads:
1927 if parent not in heads:
1922 raise util.Abort(_('working dir not at a head rev - '
1928 raise util.Abort(_('working dir not at a head rev - '
1923 'use "hg update" or merge with an explicit rev'))
1929 'use "hg update" or merge with an explicit rev'))
1924 node = parent == heads[0] and heads[-1] or heads[0]
1930 node = parent == heads[0] and heads[-1] or heads[0]
1925 return hg.merge(repo, node, force=force)
1931 return hg.merge(repo, node, force=force)
1926
1932
1927 def outgoing(ui, repo, dest=None, **opts):
1933 def outgoing(ui, repo, dest=None, **opts):
1928 """show changesets not found in destination
1934 """show changesets not found in destination
1929
1935
1930 Show changesets not found in the specified destination repository or
1936 Show changesets not found in the specified destination repository or
1931 the default push location. These are the changesets that would be pushed
1937 the default push location. These are the changesets that would be pushed
1932 if a push was requested.
1938 if a push was requested.
1933
1939
1934 See pull for valid destination format details.
1940 See pull for valid destination format details.
1935 """
1941 """
1936 dest, revs = cmdutil.parseurl(
1942 dest, revs = cmdutil.parseurl(
1937 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1943 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1938 cmdutil.setremoteconfig(ui, opts)
1944 cmdutil.setremoteconfig(ui, opts)
1939 if revs:
1945 if revs:
1940 revs = [repo.lookup(rev) for rev in revs]
1946 revs = [repo.lookup(rev) for rev in revs]
1941
1947
1942 other = hg.repository(ui, dest)
1948 other = hg.repository(ui, dest)
1943 ui.status(_('comparing with %s\n') % dest)
1949 ui.status(_('comparing with %s\n') % dest)
1944 o = repo.findoutgoing(other, force=opts['force'])
1950 o = repo.findoutgoing(other, force=opts['force'])
1945 if not o:
1951 if not o:
1946 ui.status(_("no changes found\n"))
1952 ui.status(_("no changes found\n"))
1947 return 1
1953 return 1
1948 o = repo.changelog.nodesbetween(o, revs)[0]
1954 o = repo.changelog.nodesbetween(o, revs)[0]
1949 if opts['newest_first']:
1955 if opts['newest_first']:
1950 o.reverse()
1956 o.reverse()
1951 displayer = cmdutil.show_changeset(ui, repo, opts)
1957 displayer = cmdutil.show_changeset(ui, repo, opts)
1952 for n in o:
1958 for n in o:
1953 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1959 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1954 if opts['no_merges'] and len(parents) == 2:
1960 if opts['no_merges'] and len(parents) == 2:
1955 continue
1961 continue
1956 displayer.show(changenode=n)
1962 displayer.show(changenode=n)
1957
1963
1958 def parents(ui, repo, file_=None, **opts):
1964 def parents(ui, repo, file_=None, **opts):
1959 """show the parents of the working dir or revision
1965 """show the parents of the working dir or revision
1960
1966
1961 Print the working directory's parent revisions. If a
1967 Print the working directory's parent revisions. If a
1962 revision is given via --rev, the parent of that revision
1968 revision is given via --rev, the parent of that revision
1963 will be printed. If a file argument is given, revision in
1969 will be printed. If a file argument is given, revision in
1964 which the file was last changed (before the working directory
1970 which the file was last changed (before the working directory
1965 revision or the argument to --rev if given) is printed.
1971 revision or the argument to --rev if given) is printed.
1966 """
1972 """
1967 rev = opts.get('rev')
1973 rev = opts.get('rev')
1968 if file_:
1974 if file_:
1969 ctx = repo.filectx(file_, changeid=rev)
1975 ctx = repo.filectx(file_, changeid=rev)
1970 elif rev:
1976 elif rev:
1971 ctx = repo.changectx(rev)
1977 ctx = repo.changectx(rev)
1972 else:
1978 else:
1973 ctx = repo.workingctx()
1979 ctx = repo.workingctx()
1974 p = [cp.node() for cp in ctx.parents()]
1980 p = [cp.node() for cp in ctx.parents()]
1975
1981
1976 displayer = cmdutil.show_changeset(ui, repo, opts)
1982 displayer = cmdutil.show_changeset(ui, repo, opts)
1977 for n in p:
1983 for n in p:
1978 if n != nullid:
1984 if n != nullid:
1979 displayer.show(changenode=n)
1985 displayer.show(changenode=n)
1980
1986
1981 def paths(ui, repo, search=None):
1987 def paths(ui, repo, search=None):
1982 """show definition of symbolic path names
1988 """show definition of symbolic path names
1983
1989
1984 Show definition of symbolic path name NAME. If no name is given, show
1990 Show definition of symbolic path name NAME. If no name is given, show
1985 definition of available names.
1991 definition of available names.
1986
1992
1987 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1993 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1988 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1994 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1989 """
1995 """
1990 if search:
1996 if search:
1991 for name, path in ui.configitems("paths"):
1997 for name, path in ui.configitems("paths"):
1992 if name == search:
1998 if name == search:
1993 ui.write("%s\n" % path)
1999 ui.write("%s\n" % path)
1994 return
2000 return
1995 ui.warn(_("not found!\n"))
2001 ui.warn(_("not found!\n"))
1996 return 1
2002 return 1
1997 else:
2003 else:
1998 for name, path in ui.configitems("paths"):
2004 for name, path in ui.configitems("paths"):
1999 ui.write("%s = %s\n" % (name, path))
2005 ui.write("%s = %s\n" % (name, path))
2000
2006
2001 def postincoming(ui, repo, modheads, optupdate):
2007 def postincoming(ui, repo, modheads, optupdate):
2002 if modheads == 0:
2008 if modheads == 0:
2003 return
2009 return
2004 if optupdate:
2010 if optupdate:
2005 if modheads == 1:
2011 if modheads == 1:
2006 return hg.update(repo, repo.changelog.tip()) # update
2012 return hg.update(repo, repo.changelog.tip()) # update
2007 else:
2013 else:
2008 ui.status(_("not updating, since new heads added\n"))
2014 ui.status(_("not updating, since new heads added\n"))
2009 if modheads > 1:
2015 if modheads > 1:
2010 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2016 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2011 else:
2017 else:
2012 ui.status(_("(run 'hg update' to get a working copy)\n"))
2018 ui.status(_("(run 'hg update' to get a working copy)\n"))
2013
2019
2014 def pull(ui, repo, source="default", **opts):
2020 def pull(ui, repo, source="default", **opts):
2015 """pull changes from the specified source
2021 """pull changes from the specified source
2016
2022
2017 Pull changes from a remote repository to a local one.
2023 Pull changes from a remote repository to a local one.
2018
2024
2019 This finds all changes from the repository at the specified path
2025 This finds all changes from the repository at the specified path
2020 or URL and adds them to the local repository. By default, this
2026 or URL and adds them to the local repository. By default, this
2021 does not update the copy of the project in the working directory.
2027 does not update the copy of the project in the working directory.
2022
2028
2023 Valid URLs are of the form:
2029 Valid URLs are of the form:
2024
2030
2025 local/filesystem/path (or file://local/filesystem/path)
2031 local/filesystem/path (or file://local/filesystem/path)
2026 http://[user@]host[:port]/[path]
2032 http://[user@]host[:port]/[path]
2027 https://[user@]host[:port]/[path]
2033 https://[user@]host[:port]/[path]
2028 ssh://[user@]host[:port]/[path]
2034 ssh://[user@]host[:port]/[path]
2029 static-http://host[:port]/[path]
2035 static-http://host[:port]/[path]
2030
2036
2031 Paths in the local filesystem can either point to Mercurial
2037 Paths in the local filesystem can either point to Mercurial
2032 repositories or to bundle files (as created by 'hg bundle' or
2038 repositories or to bundle files (as created by 'hg bundle' or
2033 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2039 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2034 allows access to a Mercurial repository where you simply use a web
2040 allows access to a Mercurial repository where you simply use a web
2035 server to publish the .hg directory as static content.
2041 server to publish the .hg directory as static content.
2036
2042
2037 An optional identifier after # indicates a particular branch, tag,
2043 An optional identifier after # indicates a particular branch, tag,
2038 or changeset to pull.
2044 or changeset to pull.
2039
2045
2040 Some notes about using SSH with Mercurial:
2046 Some notes about using SSH with Mercurial:
2041 - SSH requires an accessible shell account on the destination machine
2047 - SSH requires an accessible shell account on the destination machine
2042 and a copy of hg in the remote path or specified with as remotecmd.
2048 and a copy of hg in the remote path or specified with as remotecmd.
2043 - path is relative to the remote user's home directory by default.
2049 - path is relative to the remote user's home directory by default.
2044 Use an extra slash at the start of a path to specify an absolute path:
2050 Use an extra slash at the start of a path to specify an absolute path:
2045 ssh://example.com//tmp/repository
2051 ssh://example.com//tmp/repository
2046 - Mercurial doesn't use its own compression via SSH; the right thing
2052 - Mercurial doesn't use its own compression via SSH; the right thing
2047 to do is to configure it in your ~/.ssh/config, e.g.:
2053 to do is to configure it in your ~/.ssh/config, e.g.:
2048 Host *.mylocalnetwork.example.com
2054 Host *.mylocalnetwork.example.com
2049 Compression no
2055 Compression no
2050 Host *
2056 Host *
2051 Compression yes
2057 Compression yes
2052 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2058 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2053 with the --ssh command line option.
2059 with the --ssh command line option.
2054 """
2060 """
2055 source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
2061 source, revs = cmdutil.parseurl(ui.expandpath(source), opts['rev'])
2056 cmdutil.setremoteconfig(ui, opts)
2062 cmdutil.setremoteconfig(ui, opts)
2057
2063
2058 other = hg.repository(ui, source)
2064 other = hg.repository(ui, source)
2059 ui.status(_('pulling from %s\n') % (source))
2065 ui.status(_('pulling from %s\n') % (source))
2060 if revs:
2066 if revs:
2061 if 'lookup' in other.capabilities:
2067 if 'lookup' in other.capabilities:
2062 revs = [other.lookup(rev) for rev in revs]
2068 revs = [other.lookup(rev) for rev in revs]
2063 else:
2069 else:
2064 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
2070 error = _("Other repository doesn't support revision lookup, so a rev cannot be specified.")
2065 raise util.Abort(error)
2071 raise util.Abort(error)
2066
2072
2067 modheads = repo.pull(other, heads=revs, force=opts['force'])
2073 modheads = repo.pull(other, heads=revs, force=opts['force'])
2068 return postincoming(ui, repo, modheads, opts['update'])
2074 return postincoming(ui, repo, modheads, opts['update'])
2069
2075
2070 def push(ui, repo, dest=None, **opts):
2076 def push(ui, repo, dest=None, **opts):
2071 """push changes to the specified destination
2077 """push changes to the specified destination
2072
2078
2073 Push changes from the local repository to the given destination.
2079 Push changes from the local repository to the given destination.
2074
2080
2075 This is the symmetrical operation for pull. It helps to move
2081 This is the symmetrical operation for pull. It helps to move
2076 changes from the current repository to a different one. If the
2082 changes from the current repository to a different one. If the
2077 destination is local this is identical to a pull in that directory
2083 destination is local this is identical to a pull in that directory
2078 from the current one.
2084 from the current one.
2079
2085
2080 By default, push will refuse to run if it detects the result would
2086 By default, push will refuse to run if it detects the result would
2081 increase the number of remote heads. This generally indicates the
2087 increase the number of remote heads. This generally indicates the
2082 the client has forgotten to sync and merge before pushing.
2088 the client has forgotten to sync and merge before pushing.
2083
2089
2084 Valid URLs are of the form:
2090 Valid URLs are of the form:
2085
2091
2086 local/filesystem/path (or file://local/filesystem/path)
2092 local/filesystem/path (or file://local/filesystem/path)
2087 ssh://[user@]host[:port]/[path]
2093 ssh://[user@]host[:port]/[path]
2088 http://[user@]host[:port]/[path]
2094 http://[user@]host[:port]/[path]
2089 https://[user@]host[:port]/[path]
2095 https://[user@]host[:port]/[path]
2090
2096
2091 An optional identifier after # indicates a particular branch, tag,
2097 An optional identifier after # indicates a particular branch, tag,
2092 or changeset to push.
2098 or changeset to push.
2093
2099
2094 Look at the help text for the pull command for important details
2100 Look at the help text for the pull command for important details
2095 about ssh:// URLs.
2101 about ssh:// URLs.
2096
2102
2097 Pushing to http:// and https:// URLs is only possible, if this
2103 Pushing to http:// and https:// URLs is only possible, if this
2098 feature is explicitly enabled on the remote Mercurial server.
2104 feature is explicitly enabled on the remote Mercurial server.
2099 """
2105 """
2100 dest, revs = cmdutil.parseurl(
2106 dest, revs = cmdutil.parseurl(
2101 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2107 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2102 cmdutil.setremoteconfig(ui, opts)
2108 cmdutil.setremoteconfig(ui, opts)
2103
2109
2104 other = hg.repository(ui, dest)
2110 other = hg.repository(ui, dest)
2105 ui.status('pushing to %s\n' % (dest))
2111 ui.status('pushing to %s\n' % (dest))
2106 if revs:
2112 if revs:
2107 revs = [repo.lookup(rev) for rev in revs]
2113 revs = [repo.lookup(rev) for rev in revs]
2108 r = repo.push(other, opts['force'], revs=revs)
2114 r = repo.push(other, opts['force'], revs=revs)
2109 return r == 0
2115 return r == 0
2110
2116
2111 def rawcommit(ui, repo, *pats, **opts):
2117 def rawcommit(ui, repo, *pats, **opts):
2112 """raw commit interface (DEPRECATED)
2118 """raw commit interface (DEPRECATED)
2113
2119
2114 (DEPRECATED)
2120 (DEPRECATED)
2115 Lowlevel commit, for use in helper scripts.
2121 Lowlevel commit, for use in helper scripts.
2116
2122
2117 This command is not intended to be used by normal users, as it is
2123 This command is not intended to be used by normal users, as it is
2118 primarily useful for importing from other SCMs.
2124 primarily useful for importing from other SCMs.
2119
2125
2120 This command is now deprecated and will be removed in a future
2126 This command is now deprecated and will be removed in a future
2121 release, please use debugsetparents and commit instead.
2127 release, please use debugsetparents and commit instead.
2122 """
2128 """
2123
2129
2124 ui.warn(_("(the rawcommit command is deprecated)\n"))
2130 ui.warn(_("(the rawcommit command is deprecated)\n"))
2125
2131
2126 message = cmdutil.logmessage(opts)
2132 message = cmdutil.logmessage(opts)
2127
2133
2128 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2134 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2129 if opts['files']:
2135 if opts['files']:
2130 files += open(opts['files']).read().splitlines()
2136 files += open(opts['files']).read().splitlines()
2131
2137
2132 parents = [repo.lookup(p) for p in opts['parent']]
2138 parents = [repo.lookup(p) for p in opts['parent']]
2133
2139
2134 try:
2140 try:
2135 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2141 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2136 except ValueError, inst:
2142 except ValueError, inst:
2137 raise util.Abort(str(inst))
2143 raise util.Abort(str(inst))
2138
2144
2139 def recover(ui, repo):
2145 def recover(ui, repo):
2140 """roll back an interrupted transaction
2146 """roll back an interrupted transaction
2141
2147
2142 Recover from an interrupted commit or pull.
2148 Recover from an interrupted commit or pull.
2143
2149
2144 This command tries to fix the repository status after an interrupted
2150 This command tries to fix the repository status after an interrupted
2145 operation. It should only be necessary when Mercurial suggests it.
2151 operation. It should only be necessary when Mercurial suggests it.
2146 """
2152 """
2147 if repo.recover():
2153 if repo.recover():
2148 return hg.verify(repo)
2154 return hg.verify(repo)
2149 return 1
2155 return 1
2150
2156
2151 def remove(ui, repo, *pats, **opts):
2157 def remove(ui, repo, *pats, **opts):
2152 """remove the specified files on the next commit
2158 """remove the specified files on the next commit
2153
2159
2154 Schedule the indicated files for removal from the repository.
2160 Schedule the indicated files for removal from the repository.
2155
2161
2156 This only removes files from the current branch, not from the
2162 This only removes files from the current branch, not from the
2157 entire project history. If the files still exist in the working
2163 entire project history. If the files still exist in the working
2158 directory, they will be deleted from it. If invoked with --after,
2164 directory, they will be deleted from it. If invoked with --after,
2159 files are marked as removed, but not actually unlinked unless --force
2165 files are marked as removed, but not actually unlinked unless --force
2160 is also given. Without exact file names, --after will only mark
2166 is also given. Without exact file names, --after will only mark
2161 files as removed if they are no longer in the working directory.
2167 files as removed if they are no longer in the working directory.
2162
2168
2163 This command schedules the files to be removed at the next commit.
2169 This command schedules the files to be removed at the next commit.
2164 To undo a remove before that, see hg revert.
2170 To undo a remove before that, see hg revert.
2165
2171
2166 Modified files and added files are not removed by default. To
2172 Modified files and added files are not removed by default. To
2167 remove them, use the -f/--force option.
2173 remove them, use the -f/--force option.
2168 """
2174 """
2169 names = []
2175 names = []
2170 if not opts['after'] and not pats:
2176 if not opts['after'] and not pats:
2171 raise util.Abort(_('no files specified'))
2177 raise util.Abort(_('no files specified'))
2172 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2178 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2173 exact = dict.fromkeys(files)
2179 exact = dict.fromkeys(files)
2174 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2180 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2175 modified, added, removed, deleted, unknown = mardu
2181 modified, added, removed, deleted, unknown = mardu
2176 remove, forget = [], []
2182 remove, forget = [], []
2177 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2183 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2178 reason = None
2184 reason = None
2179 if abs in modified and not opts['force']:
2185 if abs in modified and not opts['force']:
2180 reason = _('is modified (use -f to force removal)')
2186 reason = _('is modified (use -f to force removal)')
2181 elif abs in added:
2187 elif abs in added:
2182 if opts['force']:
2188 if opts['force']:
2183 forget.append(abs)
2189 forget.append(abs)
2184 continue
2190 continue
2185 reason = _('has been marked for add (use -f to force removal)')
2191 reason = _('has been marked for add (use -f to force removal)')
2186 elif repo.dirstate.state(abs) == '?':
2192 elif repo.dirstate.state(abs) == '?':
2187 reason = _('is not managed')
2193 reason = _('is not managed')
2188 elif opts['after'] and not exact and abs not in deleted:
2194 elif opts['after'] and not exact and abs not in deleted:
2189 continue
2195 continue
2190 elif abs in removed:
2196 elif abs in removed:
2191 continue
2197 continue
2192 if reason:
2198 if reason:
2193 if exact:
2199 if exact:
2194 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2200 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2195 else:
2201 else:
2196 if ui.verbose or not exact:
2202 if ui.verbose or not exact:
2197 ui.status(_('removing %s\n') % rel)
2203 ui.status(_('removing %s\n') % rel)
2198 remove.append(abs)
2204 remove.append(abs)
2199 repo.forget(forget)
2205 repo.forget(forget)
2200 repo.remove(remove, unlink=opts['force'] or not opts['after'])
2206 repo.remove(remove, unlink=opts['force'] or not opts['after'])
2201
2207
2202 def rename(ui, repo, *pats, **opts):
2208 def rename(ui, repo, *pats, **opts):
2203 """rename files; equivalent of copy + remove
2209 """rename files; equivalent of copy + remove
2204
2210
2205 Mark dest as copies of sources; mark sources for deletion. If
2211 Mark dest as copies of sources; mark sources for deletion. If
2206 dest is a directory, copies are put in that directory. If dest is
2212 dest is a directory, copies are put in that directory. If dest is
2207 a file, there can only be one source.
2213 a file, there can only be one source.
2208
2214
2209 By default, this command copies the contents of files as they
2215 By default, this command copies the contents of files as they
2210 stand in the working directory. If invoked with --after, the
2216 stand in the working directory. If invoked with --after, the
2211 operation is recorded, but no copying is performed.
2217 operation is recorded, but no copying is performed.
2212
2218
2213 This command takes effect in the next commit. To undo a rename
2219 This command takes effect in the next commit. To undo a rename
2214 before that, see hg revert.
2220 before that, see hg revert.
2215 """
2221 """
2216 wlock = repo.wlock(0)
2222 wlock = repo.wlock(0)
2217 errs, copied = docopy(ui, repo, pats, opts, wlock)
2223 errs, copied = docopy(ui, repo, pats, opts, wlock)
2218 names = []
2224 names = []
2219 for abs, rel, exact in copied:
2225 for abs, rel, exact in copied:
2220 if ui.verbose or not exact:
2226 if ui.verbose or not exact:
2221 ui.status(_('removing %s\n') % rel)
2227 ui.status(_('removing %s\n') % rel)
2222 names.append(abs)
2228 names.append(abs)
2223 if not opts.get('dry_run'):
2229 if not opts.get('dry_run'):
2224 repo.remove(names, True, wlock=wlock)
2230 repo.remove(names, True, wlock=wlock)
2225 return errs
2231 return errs
2226
2232
2227 def revert(ui, repo, *pats, **opts):
2233 def revert(ui, repo, *pats, **opts):
2228 """revert files or dirs to their states as of some revision
2234 """revert files or dirs to their states as of some revision
2229
2235
2230 With no revision specified, revert the named files or directories
2236 With no revision specified, revert the named files or directories
2231 to the contents they had in the parent of the working directory.
2237 to the contents they had in the parent of the working directory.
2232 This restores the contents of the affected files to an unmodified
2238 This restores the contents of the affected files to an unmodified
2233 state and unschedules adds, removes, copies, and renames. If the
2239 state and unschedules adds, removes, copies, and renames. If the
2234 working directory has two parents, you must explicitly specify the
2240 working directory has two parents, you must explicitly specify the
2235 revision to revert to.
2241 revision to revert to.
2236
2242
2237 Modified files are saved with a .orig suffix before reverting.
2243 Modified files are saved with a .orig suffix before reverting.
2238 To disable these backups, use --no-backup.
2244 To disable these backups, use --no-backup.
2239
2245
2240 Using the -r option, revert the given files or directories to their
2246 Using the -r option, revert the given files or directories to their
2241 contents as of a specific revision. This can be helpful to "roll
2247 contents as of a specific revision. This can be helpful to "roll
2242 back" some or all of a change that should not have been committed.
2248 back" some or all of a change that should not have been committed.
2243
2249
2244 Revert modifies the working directory. It does not commit any
2250 Revert modifies the working directory. It does not commit any
2245 changes, or change the parent of the working directory. If you
2251 changes, or change the parent of the working directory. If you
2246 revert to a revision other than the parent of the working
2252 revert to a revision other than the parent of the working
2247 directory, the reverted files will thus appear modified
2253 directory, the reverted files will thus appear modified
2248 afterwards.
2254 afterwards.
2249
2255
2250 If a file has been deleted, it is recreated. If the executable
2256 If a file has been deleted, it is recreated. If the executable
2251 mode of a file was changed, it is reset.
2257 mode of a file was changed, it is reset.
2252
2258
2253 If names are given, all files matching the names are reverted.
2259 If names are given, all files matching the names are reverted.
2254
2260
2255 If no arguments are given, no files are reverted.
2261 If no arguments are given, no files are reverted.
2256 """
2262 """
2257
2263
2258 if opts["date"]:
2264 if opts["date"]:
2259 if opts["rev"]:
2265 if opts["rev"]:
2260 raise util.Abort(_("you can't specify a revision and a date"))
2266 raise util.Abort(_("you can't specify a revision and a date"))
2261 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2267 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2262
2268
2263 if not pats and not opts['all']:
2269 if not pats and not opts['all']:
2264 raise util.Abort(_('no files or directories specified; '
2270 raise util.Abort(_('no files or directories specified; '
2265 'use --all to revert the whole repo'))
2271 'use --all to revert the whole repo'))
2266
2272
2267 parent, p2 = repo.dirstate.parents()
2273 parent, p2 = repo.dirstate.parents()
2268 if not opts['rev'] and p2 != nullid:
2274 if not opts['rev'] and p2 != nullid:
2269 raise util.Abort(_('uncommitted merge - please provide a '
2275 raise util.Abort(_('uncommitted merge - please provide a '
2270 'specific revision'))
2276 'specific revision'))
2271 ctx = repo.changectx(opts['rev'])
2277 ctx = repo.changectx(opts['rev'])
2272 node = ctx.node()
2278 node = ctx.node()
2273 mf = ctx.manifest()
2279 mf = ctx.manifest()
2274 if node == parent:
2280 if node == parent:
2275 pmf = mf
2281 pmf = mf
2276 else:
2282 else:
2277 pmf = None
2283 pmf = None
2278
2284
2279 wlock = repo.wlock()
2285 wlock = repo.wlock()
2280
2286
2281 # need all matching names in dirstate and manifest of target rev,
2287 # need all matching names in dirstate and manifest of target rev,
2282 # so have to walk both. do not print errors if files exist in one
2288 # so have to walk both. do not print errors if files exist in one
2283 # but not other.
2289 # but not other.
2284
2290
2285 names = {}
2291 names = {}
2286 target_only = {}
2292 target_only = {}
2287
2293
2288 # walk dirstate.
2294 # walk dirstate.
2289
2295
2290 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2296 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2291 badmatch=mf.has_key):
2297 badmatch=mf.has_key):
2292 names[abs] = (rel, exact)
2298 names[abs] = (rel, exact)
2293 if src == 'b':
2299 if src == 'b':
2294 target_only[abs] = True
2300 target_only[abs] = True
2295
2301
2296 # walk target manifest.
2302 # walk target manifest.
2297
2303
2298 def badmatch(path):
2304 def badmatch(path):
2299 if path in names:
2305 if path in names:
2300 return True
2306 return True
2301 path_ = path + '/'
2307 path_ = path + '/'
2302 for f in names:
2308 for f in names:
2303 if f.startswith(path_):
2309 if f.startswith(path_):
2304 return True
2310 return True
2305 return False
2311 return False
2306
2312
2307 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2313 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2308 badmatch=badmatch):
2314 badmatch=badmatch):
2309 if abs in names or src == 'b':
2315 if abs in names or src == 'b':
2310 continue
2316 continue
2311 names[abs] = (rel, exact)
2317 names[abs] = (rel, exact)
2312 target_only[abs] = True
2318 target_only[abs] = True
2313
2319
2314 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2320 changes = repo.status(match=names.has_key, wlock=wlock)[:5]
2315 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2321 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2316
2322
2317 revert = ([], _('reverting %s\n'))
2323 revert = ([], _('reverting %s\n'))
2318 add = ([], _('adding %s\n'))
2324 add = ([], _('adding %s\n'))
2319 remove = ([], _('removing %s\n'))
2325 remove = ([], _('removing %s\n'))
2320 forget = ([], _('forgetting %s\n'))
2326 forget = ([], _('forgetting %s\n'))
2321 undelete = ([], _('undeleting %s\n'))
2327 undelete = ([], _('undeleting %s\n'))
2322 update = {}
2328 update = {}
2323
2329
2324 disptable = (
2330 disptable = (
2325 # dispatch table:
2331 # dispatch table:
2326 # file state
2332 # file state
2327 # action if in target manifest
2333 # action if in target manifest
2328 # action if not in target manifest
2334 # action if not in target manifest
2329 # make backup if in target manifest
2335 # make backup if in target manifest
2330 # make backup if not in target manifest
2336 # make backup if not in target manifest
2331 (modified, revert, remove, True, True),
2337 (modified, revert, remove, True, True),
2332 (added, revert, forget, True, False),
2338 (added, revert, forget, True, False),
2333 (removed, undelete, None, False, False),
2339 (removed, undelete, None, False, False),
2334 (deleted, revert, remove, False, False),
2340 (deleted, revert, remove, False, False),
2335 (unknown, add, None, True, False),
2341 (unknown, add, None, True, False),
2336 (target_only, add, None, False, False),
2342 (target_only, add, None, False, False),
2337 )
2343 )
2338
2344
2339 entries = names.items()
2345 entries = names.items()
2340 entries.sort()
2346 entries.sort()
2341
2347
2342 for abs, (rel, exact) in entries:
2348 for abs, (rel, exact) in entries:
2343 mfentry = mf.get(abs)
2349 mfentry = mf.get(abs)
2344 target = repo.wjoin(abs)
2350 target = repo.wjoin(abs)
2345 def handle(xlist, dobackup):
2351 def handle(xlist, dobackup):
2346 xlist[0].append(abs)
2352 xlist[0].append(abs)
2347 update[abs] = 1
2353 update[abs] = 1
2348 if dobackup and not opts['no_backup'] and util.lexists(target):
2354 if dobackup and not opts['no_backup'] and util.lexists(target):
2349 bakname = "%s.orig" % rel
2355 bakname = "%s.orig" % rel
2350 ui.note(_('saving current version of %s as %s\n') %
2356 ui.note(_('saving current version of %s as %s\n') %
2351 (rel, bakname))
2357 (rel, bakname))
2352 if not opts.get('dry_run'):
2358 if not opts.get('dry_run'):
2353 util.copyfile(target, bakname)
2359 util.copyfile(target, bakname)
2354 if ui.verbose or not exact:
2360 if ui.verbose or not exact:
2355 ui.status(xlist[1] % rel)
2361 ui.status(xlist[1] % rel)
2356 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2362 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2357 if abs not in table: continue
2363 if abs not in table: continue
2358 # file has changed in dirstate
2364 # file has changed in dirstate
2359 if mfentry:
2365 if mfentry:
2360 handle(hitlist, backuphit)
2366 handle(hitlist, backuphit)
2361 elif misslist is not None:
2367 elif misslist is not None:
2362 handle(misslist, backupmiss)
2368 handle(misslist, backupmiss)
2363 else:
2369 else:
2364 if exact: ui.warn(_('file not managed: %s\n') % rel)
2370 if exact: ui.warn(_('file not managed: %s\n') % rel)
2365 break
2371 break
2366 else:
2372 else:
2367 # file has not changed in dirstate
2373 # file has not changed in dirstate
2368 if node == parent:
2374 if node == parent:
2369 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2375 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2370 continue
2376 continue
2371 if pmf is None:
2377 if pmf is None:
2372 # only need parent manifest in this unlikely case,
2378 # only need parent manifest in this unlikely case,
2373 # so do not read by default
2379 # so do not read by default
2374 pmf = repo.changectx(parent).manifest()
2380 pmf = repo.changectx(parent).manifest()
2375 if abs in pmf:
2381 if abs in pmf:
2376 if mfentry:
2382 if mfentry:
2377 # if version of file is same in parent and target
2383 # if version of file is same in parent and target
2378 # manifests, do nothing
2384 # manifests, do nothing
2379 if pmf[abs] != mfentry:
2385 if pmf[abs] != mfentry:
2380 handle(revert, False)
2386 handle(revert, False)
2381 else:
2387 else:
2382 handle(remove, False)
2388 handle(remove, False)
2383
2389
2384 if not opts.get('dry_run'):
2390 if not opts.get('dry_run'):
2385 repo.dirstate.forget(forget[0])
2391 repo.dirstate.forget(forget[0])
2386 r = hg.revert(repo, node, update.has_key, wlock)
2392 r = hg.revert(repo, node, update.has_key, wlock)
2387 repo.dirstate.update(add[0], 'a')
2393 repo.dirstate.update(add[0], 'a')
2388 repo.dirstate.update(undelete[0], 'n')
2394 repo.dirstate.update(undelete[0], 'n')
2389 repo.dirstate.update(remove[0], 'r')
2395 repo.dirstate.update(remove[0], 'r')
2390 return r
2396 return r
2391
2397
2392 def rollback(ui, repo):
2398 def rollback(ui, repo):
2393 """roll back the last transaction in this repository
2399 """roll back the last transaction in this repository
2394
2400
2395 Roll back the last transaction in this repository, restoring the
2401 Roll back the last transaction in this repository, restoring the
2396 project to its state prior to the transaction.
2402 project to its state prior to the transaction.
2397
2403
2398 Transactions are used to encapsulate the effects of all commands
2404 Transactions are used to encapsulate the effects of all commands
2399 that create new changesets or propagate existing changesets into a
2405 that create new changesets or propagate existing changesets into a
2400 repository. For example, the following commands are transactional,
2406 repository. For example, the following commands are transactional,
2401 and their effects can be rolled back:
2407 and their effects can be rolled back:
2402
2408
2403 commit
2409 commit
2404 import
2410 import
2405 pull
2411 pull
2406 push (with this repository as destination)
2412 push (with this repository as destination)
2407 unbundle
2413 unbundle
2408
2414
2409 This command should be used with care. There is only one level of
2415 This command should be used with care. There is only one level of
2410 rollback, and there is no way to undo a rollback. It will also
2416 rollback, and there is no way to undo a rollback. It will also
2411 restore the dirstate at the time of the last transaction, which
2417 restore the dirstate at the time of the last transaction, which
2412 may lose subsequent dirstate changes.
2418 may lose subsequent dirstate changes.
2413
2419
2414 This command is not intended for use on public repositories. Once
2420 This command is not intended for use on public repositories. Once
2415 changes are visible for pull by other users, rolling a transaction
2421 changes are visible for pull by other users, rolling a transaction
2416 back locally is ineffective (someone else may already have pulled
2422 back locally is ineffective (someone else may already have pulled
2417 the changes). Furthermore, a race is possible with readers of the
2423 the changes). Furthermore, a race is possible with readers of the
2418 repository; for example an in-progress pull from the repository
2424 repository; for example an in-progress pull from the repository
2419 may fail if a rollback is performed.
2425 may fail if a rollback is performed.
2420 """
2426 """
2421 repo.rollback()
2427 repo.rollback()
2422
2428
2423 def root(ui, repo):
2429 def root(ui, repo):
2424 """print the root (top) of the current working dir
2430 """print the root (top) of the current working dir
2425
2431
2426 Print the root directory of the current repository.
2432 Print the root directory of the current repository.
2427 """
2433 """
2428 ui.write(repo.root + "\n")
2434 ui.write(repo.root + "\n")
2429
2435
2430 def serve(ui, repo, **opts):
2436 def serve(ui, repo, **opts):
2431 """export the repository via HTTP
2437 """export the repository via HTTP
2432
2438
2433 Start a local HTTP repository browser and pull server.
2439 Start a local HTTP repository browser and pull server.
2434
2440
2435 By default, the server logs accesses to stdout and errors to
2441 By default, the server logs accesses to stdout and errors to
2436 stderr. Use the "-A" and "-E" options to log to files.
2442 stderr. Use the "-A" and "-E" options to log to files.
2437 """
2443 """
2438
2444
2439 if opts["stdio"]:
2445 if opts["stdio"]:
2440 if repo is None:
2446 if repo is None:
2441 raise hg.RepoError(_("There is no Mercurial repository here"
2447 raise hg.RepoError(_("There is no Mercurial repository here"
2442 " (.hg not found)"))
2448 " (.hg not found)"))
2443 s = sshserver.sshserver(ui, repo)
2449 s = sshserver.sshserver(ui, repo)
2444 s.serve_forever()
2450 s.serve_forever()
2445
2451
2446 parentui = ui.parentui or ui
2452 parentui = ui.parentui or ui
2447 optlist = ("name templates style address port ipv6"
2453 optlist = ("name templates style address port ipv6"
2448 " accesslog errorlog webdir_conf")
2454 " accesslog errorlog webdir_conf")
2449 for o in optlist.split():
2455 for o in optlist.split():
2450 if opts[o]:
2456 if opts[o]:
2451 parentui.setconfig("web", o, str(opts[o]))
2457 parentui.setconfig("web", o, str(opts[o]))
2452
2458
2453 if repo is None and not ui.config("web", "webdir_conf"):
2459 if repo is None and not ui.config("web", "webdir_conf"):
2454 raise hg.RepoError(_("There is no Mercurial repository here"
2460 raise hg.RepoError(_("There is no Mercurial repository here"
2455 " (.hg not found)"))
2461 " (.hg not found)"))
2456
2462
2457 class service:
2463 class service:
2458 def init(self):
2464 def init(self):
2459 util.set_signal_handler()
2465 util.set_signal_handler()
2460 try:
2466 try:
2461 self.httpd = hgweb.server.create_server(parentui, repo)
2467 self.httpd = hgweb.server.create_server(parentui, repo)
2462 except socket.error, inst:
2468 except socket.error, inst:
2463 raise util.Abort(_('cannot start server: ') + inst.args[1])
2469 raise util.Abort(_('cannot start server: ') + inst.args[1])
2464
2470
2465 if not ui.verbose: return
2471 if not ui.verbose: return
2466
2472
2467 if self.httpd.port != 80:
2473 if self.httpd.port != 80:
2468 ui.status(_('listening at http://%s:%d/\n') %
2474 ui.status(_('listening at http://%s:%d/\n') %
2469 (self.httpd.addr, self.httpd.port))
2475 (self.httpd.addr, self.httpd.port))
2470 else:
2476 else:
2471 ui.status(_('listening at http://%s/\n') % self.httpd.addr)
2477 ui.status(_('listening at http://%s/\n') % self.httpd.addr)
2472
2478
2473 def run(self):
2479 def run(self):
2474 self.httpd.serve_forever()
2480 self.httpd.serve_forever()
2475
2481
2476 service = service()
2482 service = service()
2477
2483
2478 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2484 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2479
2485
2480 def status(ui, repo, *pats, **opts):
2486 def status(ui, repo, *pats, **opts):
2481 """show changed files in the working directory
2487 """show changed files in the working directory
2482
2488
2483 Show status of files in the repository. If names are given, only
2489 Show status of files in the repository. If names are given, only
2484 files that match are shown. Files that are clean or ignored, are
2490 files that match are shown. Files that are clean or ignored, are
2485 not listed unless -c (clean), -i (ignored) or -A is given.
2491 not listed unless -c (clean), -i (ignored) or -A is given.
2486
2492
2487 NOTE: status may appear to disagree with diff if permissions have
2493 NOTE: status may appear to disagree with diff if permissions have
2488 changed or a merge has occurred. The standard diff format does not
2494 changed or a merge has occurred. The standard diff format does not
2489 report permission changes and diff only reports changes relative
2495 report permission changes and diff only reports changes relative
2490 to one merge parent.
2496 to one merge parent.
2491
2497
2492 If one revision is given, it is used as the base revision.
2498 If one revision is given, it is used as the base revision.
2493 If two revisions are given, the difference between them is shown.
2499 If two revisions are given, the difference between them is shown.
2494
2500
2495 The codes used to show the status of files are:
2501 The codes used to show the status of files are:
2496 M = modified
2502 M = modified
2497 A = added
2503 A = added
2498 R = removed
2504 R = removed
2499 C = clean
2505 C = clean
2500 ! = deleted, but still tracked
2506 ! = deleted, but still tracked
2501 ? = not tracked
2507 ? = not tracked
2502 I = ignored (not shown by default)
2508 I = ignored (not shown by default)
2503 = the previous added file was copied from here
2509 = the previous added file was copied from here
2504 """
2510 """
2505
2511
2506 all = opts['all']
2512 all = opts['all']
2507 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2513 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2508
2514
2509 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2515 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2510 cwd = (pats and repo.getcwd()) or ''
2516 cwd = (pats and repo.getcwd()) or ''
2511 modified, added, removed, deleted, unknown, ignored, clean = [
2517 modified, added, removed, deleted, unknown, ignored, clean = [
2512 n for n in repo.status(node1=node1, node2=node2, files=files,
2518 n for n in repo.status(node1=node1, node2=node2, files=files,
2513 match=matchfn,
2519 match=matchfn,
2514 list_ignored=all or opts['ignored'],
2520 list_ignored=all or opts['ignored'],
2515 list_clean=all or opts['clean'])]
2521 list_clean=all or opts['clean'])]
2516
2522
2517 changetypes = (('modified', 'M', modified),
2523 changetypes = (('modified', 'M', modified),
2518 ('added', 'A', added),
2524 ('added', 'A', added),
2519 ('removed', 'R', removed),
2525 ('removed', 'R', removed),
2520 ('deleted', '!', deleted),
2526 ('deleted', '!', deleted),
2521 ('unknown', '?', unknown),
2527 ('unknown', '?', unknown),
2522 ('ignored', 'I', ignored))
2528 ('ignored', 'I', ignored))
2523
2529
2524 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2530 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2525
2531
2526 end = opts['print0'] and '\0' or '\n'
2532 end = opts['print0'] and '\0' or '\n'
2527
2533
2528 for opt, char, changes in ([ct for ct in explicit_changetypes
2534 for opt, char, changes in ([ct for ct in explicit_changetypes
2529 if all or opts[ct[0]]]
2535 if all or opts[ct[0]]]
2530 or changetypes):
2536 or changetypes):
2531 if opts['no_status']:
2537 if opts['no_status']:
2532 format = "%%s%s" % end
2538 format = "%%s%s" % end
2533 else:
2539 else:
2534 format = "%s %%s%s" % (char, end)
2540 format = "%s %%s%s" % (char, end)
2535
2541
2536 for f in changes:
2542 for f in changes:
2537 ui.write(format % repo.pathto(f, cwd))
2543 ui.write(format % repo.pathto(f, cwd))
2538 if ((all or opts.get('copies')) and not opts.get('no_status')):
2544 if ((all or opts.get('copies')) and not opts.get('no_status')):
2539 copied = repo.dirstate.copied(f)
2545 copied = repo.dirstate.copied(f)
2540 if copied:
2546 if copied:
2541 ui.write(' %s%s' % (repo.pathto(copied, cwd), end))
2547 ui.write(' %s%s' % (repo.pathto(copied, cwd), end))
2542
2548
2543 def tag(ui, repo, name, rev_=None, **opts):
2549 def tag(ui, repo, name, rev_=None, **opts):
2544 """add a tag for the current or given revision
2550 """add a tag for the current or given revision
2545
2551
2546 Name a particular revision using <name>.
2552 Name a particular revision using <name>.
2547
2553
2548 Tags are used to name particular revisions of the repository and are
2554 Tags are used to name particular revisions of the repository and are
2549 very useful to compare different revision, to go back to significant
2555 very useful to compare different revision, to go back to significant
2550 earlier versions or to mark branch points as releases, etc.
2556 earlier versions or to mark branch points as releases, etc.
2551
2557
2552 If no revision is given, the parent of the working directory is used,
2558 If no revision is given, the parent of the working directory is used,
2553 or tip if no revision is checked out.
2559 or tip if no revision is checked out.
2554
2560
2555 To facilitate version control, distribution, and merging of tags,
2561 To facilitate version control, distribution, and merging of tags,
2556 they are stored as a file named ".hgtags" which is managed
2562 they are stored as a file named ".hgtags" which is managed
2557 similarly to other project files and can be hand-edited if
2563 similarly to other project files and can be hand-edited if
2558 necessary. The file '.hg/localtags' is used for local tags (not
2564 necessary. The file '.hg/localtags' is used for local tags (not
2559 shared among repositories).
2565 shared among repositories).
2560 """
2566 """
2561 if name in ['tip', '.', 'null']:
2567 if name in ['tip', '.', 'null']:
2562 raise util.Abort(_("the name '%s' is reserved") % name)
2568 raise util.Abort(_("the name '%s' is reserved") % name)
2563 if rev_ is not None:
2569 if rev_ is not None:
2564 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2570 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2565 "please use 'hg tag [-r REV] NAME' instead\n"))
2571 "please use 'hg tag [-r REV] NAME' instead\n"))
2566 if opts['rev']:
2572 if opts['rev']:
2567 raise util.Abort(_("use only one form to specify the revision"))
2573 raise util.Abort(_("use only one form to specify the revision"))
2568 if opts['rev'] and opts['remove']:
2574 if opts['rev'] and opts['remove']:
2569 raise util.Abort(_("--rev and --remove are incompatible"))
2575 raise util.Abort(_("--rev and --remove are incompatible"))
2570 if opts['rev']:
2576 if opts['rev']:
2571 rev_ = opts['rev']
2577 rev_ = opts['rev']
2572 message = opts['message']
2578 message = opts['message']
2573 if opts['remove']:
2579 if opts['remove']:
2574 if not name in repo.tags():
2580 if not name in repo.tags():
2575 raise util.Abort(_('tag %s does not exist') % name)
2581 raise util.Abort(_('tag %s does not exist') % name)
2576 rev_ = nullid
2582 rev_ = nullid
2577 if not message:
2583 if not message:
2578 message = _('Removed tag %s') % name
2584 message = _('Removed tag %s') % name
2579 elif name in repo.tags() and not opts['force']:
2585 elif name in repo.tags() and not opts['force']:
2580 raise util.Abort(_('a tag named %s already exists (use -f to force)')
2586 raise util.Abort(_('a tag named %s already exists (use -f to force)')
2581 % name)
2587 % name)
2582 if not rev_ and repo.dirstate.parents()[1] != nullid:
2588 if not rev_ and repo.dirstate.parents()[1] != nullid:
2583 raise util.Abort(_('uncommitted merge - please provide a '
2589 raise util.Abort(_('uncommitted merge - please provide a '
2584 'specific revision'))
2590 'specific revision'))
2585 r = repo.changectx(rev_).node()
2591 r = repo.changectx(rev_).node()
2586
2592
2587 if not message:
2593 if not message:
2588 message = _('Added tag %s for changeset %s') % (name, short(r))
2594 message = _('Added tag %s for changeset %s') % (name, short(r))
2589
2595
2590 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2596 repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
2591
2597
2592 def tags(ui, repo):
2598 def tags(ui, repo):
2593 """list repository tags
2599 """list repository tags
2594
2600
2595 List the repository tags.
2601 List the repository tags.
2596
2602
2597 This lists both regular and local tags.
2603 This lists both regular and local tags.
2598 """
2604 """
2599
2605
2600 l = repo.tagslist()
2606 l = repo.tagslist()
2601 l.reverse()
2607 l.reverse()
2602 hexfunc = ui.debugflag and hex or short
2608 hexfunc = ui.debugflag and hex or short
2603 for t, n in l:
2609 for t, n in l:
2604 try:
2610 try:
2605 hn = hexfunc(n)
2611 hn = hexfunc(n)
2606 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2612 r = "%5d:%s" % (repo.changelog.rev(n), hexfunc(n))
2607 except revlog.LookupError:
2613 except revlog.LookupError:
2608 r = " ?:%s" % hn
2614 r = " ?:%s" % hn
2609 if ui.quiet:
2615 if ui.quiet:
2610 ui.write("%s\n" % t)
2616 ui.write("%s\n" % t)
2611 else:
2617 else:
2612 spaces = " " * (30 - util.locallen(t))
2618 spaces = " " * (30 - util.locallen(t))
2613 ui.write("%s%s %s\n" % (t, spaces, r))
2619 ui.write("%s%s %s\n" % (t, spaces, r))
2614
2620
2615 def tip(ui, repo, **opts):
2621 def tip(ui, repo, **opts):
2616 """show the tip revision
2622 """show the tip revision
2617
2623
2618 Show the tip revision.
2624 Show the tip revision.
2619 """
2625 """
2620 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2626 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2621
2627
2622 def unbundle(ui, repo, fname1, *fnames, **opts):
2628 def unbundle(ui, repo, fname1, *fnames, **opts):
2623 """apply one or more changegroup files
2629 """apply one or more changegroup files
2624
2630
2625 Apply one or more compressed changegroup files generated by the
2631 Apply one or more compressed changegroup files generated by the
2626 bundle command.
2632 bundle command.
2627 """
2633 """
2628 fnames = (fname1,) + fnames
2634 fnames = (fname1,) + fnames
2629 result = None
2635 result = None
2630 for fname in fnames:
2636 for fname in fnames:
2631 if os.path.exists(fname):
2637 if os.path.exists(fname):
2632 f = open(fname, "rb")
2638 f = open(fname, "rb")
2633 else:
2639 else:
2634 f = urllib.urlopen(fname)
2640 f = urllib.urlopen(fname)
2635 gen = changegroup.readbundle(f, fname)
2641 gen = changegroup.readbundle(f, fname)
2636 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2642 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2637
2643
2638 return postincoming(ui, repo, modheads, opts['update'])
2644 return postincoming(ui, repo, modheads, opts['update'])
2639
2645
2640 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2646 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2641 """update working directory
2647 """update working directory
2642
2648
2643 Update the working directory to the specified revision, or the
2649 Update the working directory to the specified revision, or the
2644 tip of the current branch if none is specified.
2650 tip of the current branch if none is specified.
2645
2651
2646 If there are no outstanding changes in the working directory and
2652 If there are no outstanding changes in the working directory and
2647 there is a linear relationship between the current version and the
2653 there is a linear relationship between the current version and the
2648 requested version, the result is the requested version.
2654 requested version, the result is the requested version.
2649
2655
2650 To merge the working directory with another revision, use the
2656 To merge the working directory with another revision, use the
2651 merge command.
2657 merge command.
2652
2658
2653 By default, update will refuse to run if doing so would require
2659 By default, update will refuse to run if doing so would require
2654 discarding local changes.
2660 discarding local changes.
2655 """
2661 """
2656 if rev and node:
2662 if rev and node:
2657 raise util.Abort(_("please specify just one revision"))
2663 raise util.Abort(_("please specify just one revision"))
2658
2664
2659 if not rev:
2665 if not rev:
2660 rev = node
2666 rev = node
2661
2667
2662 if date:
2668 if date:
2663 if rev:
2669 if rev:
2664 raise util.Abort(_("you can't specify a revision and a date"))
2670 raise util.Abort(_("you can't specify a revision and a date"))
2665 rev = cmdutil.finddate(ui, repo, date)
2671 rev = cmdutil.finddate(ui, repo, date)
2666
2672
2667 if clean:
2673 if clean:
2668 return hg.clean(repo, rev)
2674 return hg.clean(repo, rev)
2669 else:
2675 else:
2670 return hg.update(repo, rev)
2676 return hg.update(repo, rev)
2671
2677
2672 def verify(ui, repo):
2678 def verify(ui, repo):
2673 """verify the integrity of the repository
2679 """verify the integrity of the repository
2674
2680
2675 Verify the integrity of the current repository.
2681 Verify the integrity of the current repository.
2676
2682
2677 This will perform an extensive check of the repository's
2683 This will perform an extensive check of the repository's
2678 integrity, validating the hashes and checksums of each entry in
2684 integrity, validating the hashes and checksums of each entry in
2679 the changelog, manifest, and tracked files, as well as the
2685 the changelog, manifest, and tracked files, as well as the
2680 integrity of their crosslinks and indices.
2686 integrity of their crosslinks and indices.
2681 """
2687 """
2682 return hg.verify(repo)
2688 return hg.verify(repo)
2683
2689
2684 def version_(ui):
2690 def version_(ui):
2685 """output version and copyright information"""
2691 """output version and copyright information"""
2686 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2692 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2687 % version.get_version())
2693 % version.get_version())
2688 ui.status(_(
2694 ui.status(_(
2689 "\nCopyright (C) 2005-2007 Matt Mackall <mpm@selenic.com> and others\n"
2695 "\nCopyright (C) 2005-2007 Matt Mackall <mpm@selenic.com> and others\n"
2690 "This is free software; see the source for copying conditions. "
2696 "This is free software; see the source for copying conditions. "
2691 "There is NO\nwarranty; "
2697 "There is NO\nwarranty; "
2692 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2698 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2693 ))
2699 ))
2694
2700
2695 # Command options and aliases are listed here, alphabetically
2701 # Command options and aliases are listed here, alphabetically
2696
2702
2697 globalopts = [
2703 globalopts = [
2698 ('R', 'repository', '',
2704 ('R', 'repository', '',
2699 _('repository root directory or symbolic path name')),
2705 _('repository root directory or symbolic path name')),
2700 ('', 'cwd', '', _('change working directory')),
2706 ('', 'cwd', '', _('change working directory')),
2701 ('y', 'noninteractive', None,
2707 ('y', 'noninteractive', None,
2702 _('do not prompt, assume \'yes\' for any required answers')),
2708 _('do not prompt, assume \'yes\' for any required answers')),
2703 ('q', 'quiet', None, _('suppress output')),
2709 ('q', 'quiet', None, _('suppress output')),
2704 ('v', 'verbose', None, _('enable additional output')),
2710 ('v', 'verbose', None, _('enable additional output')),
2705 ('', 'config', [], _('set/override config option')),
2711 ('', 'config', [], _('set/override config option')),
2706 ('', 'debug', None, _('enable debugging output')),
2712 ('', 'debug', None, _('enable debugging output')),
2707 ('', 'debugger', None, _('start debugger')),
2713 ('', 'debugger', None, _('start debugger')),
2708 ('', 'encoding', util._encoding, _('set the charset encoding')),
2714 ('', 'encoding', util._encoding, _('set the charset encoding')),
2709 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2715 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2710 ('', 'lsprof', None, _('print improved command execution profile')),
2716 ('', 'lsprof', None, _('print improved command execution profile')),
2711 ('', 'traceback', None, _('print traceback on exception')),
2717 ('', 'traceback', None, _('print traceback on exception')),
2712 ('', 'time', None, _('time how long the command takes')),
2718 ('', 'time', None, _('time how long the command takes')),
2713 ('', 'profile', None, _('print command execution profile')),
2719 ('', 'profile', None, _('print command execution profile')),
2714 ('', 'version', None, _('output version information and exit')),
2720 ('', 'version', None, _('output version information and exit')),
2715 ('h', 'help', None, _('display help and exit')),
2721 ('h', 'help', None, _('display help and exit')),
2716 ]
2722 ]
2717
2723
2718 dryrunopts = [('n', 'dry-run', None,
2724 dryrunopts = [('n', 'dry-run', None,
2719 _('do not perform actions, just print output'))]
2725 _('do not perform actions, just print output'))]
2720
2726
2721 remoteopts = [
2727 remoteopts = [
2722 ('e', 'ssh', '', _('specify ssh command to use')),
2728 ('e', 'ssh', '', _('specify ssh command to use')),
2723 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2729 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2724 ]
2730 ]
2725
2731
2726 walkopts = [
2732 walkopts = [
2727 ('I', 'include', [], _('include names matching the given patterns')),
2733 ('I', 'include', [], _('include names matching the given patterns')),
2728 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2734 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2729 ]
2735 ]
2730
2736
2731 commitopts = [
2737 commitopts = [
2732 ('m', 'message', '', _('use <text> as commit message')),
2738 ('m', 'message', '', _('use <text> as commit message')),
2733 ('l', 'logfile', '', _('read commit message from <file>')),
2739 ('l', 'logfile', '', _('read commit message from <file>')),
2734 ]
2740 ]
2735
2741
2736 table = {
2742 table = {
2737 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2743 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2738 "addremove":
2744 "addremove":
2739 (addremove,
2745 (addremove,
2740 [('s', 'similarity', '',
2746 [('s', 'similarity', '',
2741 _('guess renamed files by similarity (0<=s<=100)')),
2747 _('guess renamed files by similarity (0<=s<=100)')),
2742 ] + walkopts + dryrunopts,
2748 ] + walkopts + dryrunopts,
2743 _('hg addremove [OPTION]... [FILE]...')),
2749 _('hg addremove [OPTION]... [FILE]...')),
2744 "^annotate":
2750 "^annotate":
2745 (annotate,
2751 (annotate,
2746 [('r', 'rev', '', _('annotate the specified revision')),
2752 [('r', 'rev', '', _('annotate the specified revision')),
2747 ('f', 'follow', None, _('follow file copies and renames')),
2753 ('f', 'follow', None, _('follow file copies and renames')),
2748 ('a', 'text', None, _('treat all files as text')),
2754 ('a', 'text', None, _('treat all files as text')),
2749 ('u', 'user', None, _('list the author')),
2755 ('u', 'user', None, _('list the author')),
2750 ('d', 'date', None, _('list the date')),
2756 ('d', 'date', None, _('list the date')),
2751 ('n', 'number', None, _('list the revision number (default)')),
2757 ('n', 'number', None, _('list the revision number (default)')),
2752 ('c', 'changeset', None, _('list the changeset')),
2758 ('c', 'changeset', None, _('list the changeset')),
2753 ] + walkopts,
2759 ] + walkopts,
2754 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2760 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] FILE...')),
2755 "archive":
2761 "archive":
2756 (archive,
2762 (archive,
2757 [('', 'no-decode', None, _('do not pass files through decoders')),
2763 [('', 'no-decode', None, _('do not pass files through decoders')),
2758 ('p', 'prefix', '', _('directory prefix for files in archive')),
2764 ('p', 'prefix', '', _('directory prefix for files in archive')),
2759 ('r', 'rev', '', _('revision to distribute')),
2765 ('r', 'rev', '', _('revision to distribute')),
2760 ('t', 'type', '', _('type of distribution to create')),
2766 ('t', 'type', '', _('type of distribution to create')),
2761 ] + walkopts,
2767 ] + walkopts,
2762 _('hg archive [OPTION]... DEST')),
2768 _('hg archive [OPTION]... DEST')),
2763 "backout":
2769 "backout":
2764 (backout,
2770 (backout,
2765 [('', 'merge', None,
2771 [('', 'merge', None,
2766 _('merge with old dirstate parent after backout')),
2772 _('merge with old dirstate parent after backout')),
2767 ('d', 'date', '', _('record datecode as commit date')),
2773 ('d', 'date', '', _('record datecode as commit date')),
2768 ('', 'parent', '', _('parent to choose when backing out merge')),
2774 ('', 'parent', '', _('parent to choose when backing out merge')),
2769 ('u', 'user', '', _('record user as committer')),
2775 ('u', 'user', '', _('record user as committer')),
2770 ('r', 'rev', '', _('revision to backout')),
2776 ('r', 'rev', '', _('revision to backout')),
2771 ] + walkopts + commitopts,
2777 ] + walkopts + commitopts,
2772 _('hg backout [OPTION]... [-r] REV')),
2778 _('hg backout [OPTION]... [-r] REV')),
2773 "branch":
2779 "branch":
2774 (branch,
2780 (branch,
2775 [('f', 'force', None,
2781 [('f', 'force', None,
2776 _('set branch name even if it shadows an existing branch'))],
2782 _('set branch name even if it shadows an existing branch'))],
2777 _('hg branch [NAME]')),
2783 _('hg branch [NAME]')),
2778 "branches":
2784 "branches":
2779 (branches,
2785 (branches,
2780 [('a', 'active', False,
2786 [('a', 'active', False,
2781 _('show only branches that have unmerged heads'))],
2787 _('show only branches that have unmerged heads'))],
2782 _('hg branches [-a]')),
2788 _('hg branches [-a]')),
2783 "bundle":
2789 "bundle":
2784 (bundle,
2790 (bundle,
2785 [('f', 'force', None,
2791 [('f', 'force', None,
2786 _('run even when remote repository is unrelated')),
2792 _('run even when remote repository is unrelated')),
2787 ('r', 'rev', [],
2793 ('r', 'rev', [],
2788 _('a changeset you would like to bundle')),
2794 _('a changeset you would like to bundle')),
2789 ('', 'base', [],
2795 ('', 'base', [],
2790 _('a base changeset to specify instead of a destination')),
2796 _('a base changeset to specify instead of a destination')),
2791 ] + remoteopts,
2797 ] + remoteopts,
2792 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2798 _('hg bundle [-f] [-r REV]... [--base REV]... FILE [DEST]')),
2793 "cat":
2799 "cat":
2794 (cat,
2800 (cat,
2795 [('o', 'output', '', _('print output to file with formatted name')),
2801 [('o', 'output', '', _('print output to file with formatted name')),
2796 ('r', 'rev', '', _('print the given revision')),
2802 ('r', 'rev', '', _('print the given revision')),
2797 ] + walkopts,
2803 ] + walkopts,
2798 _('hg cat [OPTION]... FILE...')),
2804 _('hg cat [OPTION]... FILE...')),
2799 "^clone":
2805 "^clone":
2800 (clone,
2806 (clone,
2801 [('U', 'noupdate', None, _('do not update the new working directory')),
2807 [('U', 'noupdate', None, _('do not update the new working directory')),
2802 ('r', 'rev', [],
2808 ('r', 'rev', [],
2803 _('a changeset you would like to have after cloning')),
2809 _('a changeset you would like to have after cloning')),
2804 ('', 'pull', None, _('use pull protocol to copy metadata')),
2810 ('', 'pull', None, _('use pull protocol to copy metadata')),
2805 ('', 'uncompressed', None,
2811 ('', 'uncompressed', None,
2806 _('use uncompressed transfer (fast over LAN)')),
2812 _('use uncompressed transfer (fast over LAN)')),
2807 ] + remoteopts,
2813 ] + remoteopts,
2808 _('hg clone [OPTION]... SOURCE [DEST]')),
2814 _('hg clone [OPTION]... SOURCE [DEST]')),
2809 "^commit|ci":
2815 "^commit|ci":
2810 (commit,
2816 (commit,
2811 [('A', 'addremove', None,
2817 [('A', 'addremove', None,
2812 _('mark new/missing files as added/removed before committing')),
2818 _('mark new/missing files as added/removed before committing')),
2813 ('d', 'date', '', _('record datecode as commit date')),
2819 ('d', 'date', '', _('record datecode as commit date')),
2814 ('u', 'user', '', _('record user as commiter')),
2820 ('u', 'user', '', _('record user as commiter')),
2815 ] + walkopts + commitopts,
2821 ] + walkopts + commitopts,
2816 _('hg commit [OPTION]... [FILE]...')),
2822 _('hg commit [OPTION]... [FILE]...')),
2817 "copy|cp":
2823 "copy|cp":
2818 (copy,
2824 (copy,
2819 [('A', 'after', None, _('record a copy that has already occurred')),
2825 [('A', 'after', None, _('record a copy that has already occurred')),
2820 ('f', 'force', None,
2826 ('f', 'force', None,
2821 _('forcibly copy over an existing managed file')),
2827 _('forcibly copy over an existing managed file')),
2822 ] + walkopts + dryrunopts,
2828 ] + walkopts + dryrunopts,
2823 _('hg copy [OPTION]... [SOURCE]... DEST')),
2829 _('hg copy [OPTION]... [SOURCE]... DEST')),
2824 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2830 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2825 "debugcomplete":
2831 "debugcomplete":
2826 (debugcomplete,
2832 (debugcomplete,
2827 [('o', 'options', None, _('show the command options'))],
2833 [('o', 'options', None, _('show the command options'))],
2828 _('debugcomplete [-o] CMD')),
2834 _('debugcomplete [-o] CMD')),
2829 "debuginstall": (debuginstall, [], _('debuginstall')),
2835 "debuginstall": (debuginstall, [], _('debuginstall')),
2830 "debugrebuildstate":
2836 "debugrebuildstate":
2831 (debugrebuildstate,
2837 (debugrebuildstate,
2832 [('r', 'rev', '', _('revision to rebuild to'))],
2838 [('r', 'rev', '', _('revision to rebuild to'))],
2833 _('debugrebuildstate [-r REV] [REV]')),
2839 _('debugrebuildstate [-r REV] [REV]')),
2834 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2840 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2835 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2841 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2836 "debugstate": (debugstate, [], _('debugstate')),
2842 "debugstate": (debugstate, [], _('debugstate')),
2837 "debugdate":
2843 "debugdate":
2838 (debugdate,
2844 (debugdate,
2839 [('e', 'extended', None, _('try extended date formats'))],
2845 [('e', 'extended', None, _('try extended date formats'))],
2840 _('debugdate [-e] DATE [RANGE]')),
2846 _('debugdate [-e] DATE [RANGE]')),
2841 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2847 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2842 "debugindex": (debugindex, [], _('debugindex FILE')),
2848 "debugindex": (debugindex, [], _('debugindex FILE')),
2843 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2849 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2844 "debugrename":
2850 "debugrename":
2845 (debugrename,
2851 (debugrename,
2846 [('r', 'rev', '', _('revision to debug'))],
2852 [('r', 'rev', '', _('revision to debug'))],
2847 _('debugrename [-r REV] FILE')),
2853 _('debugrename [-r REV] FILE')),
2848 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2854 "debugwalk": (debugwalk, walkopts, _('debugwalk [OPTION]... [FILE]...')),
2849 "^diff":
2855 "^diff":
2850 (diff,
2856 (diff,
2851 [('r', 'rev', [], _('revision')),
2857 [('r', 'rev', [], _('revision')),
2852 ('a', 'text', None, _('treat all files as text')),
2858 ('a', 'text', None, _('treat all files as text')),
2853 ('p', 'show-function', None,
2859 ('p', 'show-function', None,
2854 _('show which function each change is in')),
2860 _('show which function each change is in')),
2855 ('g', 'git', None, _('use git extended diff format')),
2861 ('g', 'git', None, _('use git extended diff format')),
2856 ('', 'nodates', None, _("don't include dates in diff headers")),
2862 ('', 'nodates', None, _("don't include dates in diff headers")),
2857 ('w', 'ignore-all-space', None,
2863 ('w', 'ignore-all-space', None,
2858 _('ignore white space when comparing lines')),
2864 _('ignore white space when comparing lines')),
2859 ('b', 'ignore-space-change', None,
2865 ('b', 'ignore-space-change', None,
2860 _('ignore changes in the amount of white space')),
2866 _('ignore changes in the amount of white space')),
2861 ('B', 'ignore-blank-lines', None,
2867 ('B', 'ignore-blank-lines', None,
2862 _('ignore changes whose lines are all blank')),
2868 _('ignore changes whose lines are all blank')),
2863 ] + walkopts,
2869 ] + walkopts,
2864 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2870 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
2865 "^export":
2871 "^export":
2866 (export,
2872 (export,
2867 [('o', 'output', '', _('print output to file with formatted name')),
2873 [('o', 'output', '', _('print output to file with formatted name')),
2868 ('a', 'text', None, _('treat all files as text')),
2874 ('a', 'text', None, _('treat all files as text')),
2869 ('g', 'git', None, _('use git extended diff format')),
2875 ('g', 'git', None, _('use git extended diff format')),
2870 ('', 'nodates', None, _("don't include dates in diff headers")),
2876 ('', 'nodates', None, _("don't include dates in diff headers")),
2871 ('', 'switch-parent', None, _('diff against the second parent'))],
2877 ('', 'switch-parent', None, _('diff against the second parent'))],
2872 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2878 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
2873 "grep":
2879 "grep":
2874 (grep,
2880 (grep,
2875 [('0', 'print0', None, _('end fields with NUL')),
2881 [('0', 'print0', None, _('end fields with NUL')),
2876 ('', 'all', None, _('print all revisions that match')),
2882 ('', 'all', None, _('print all revisions that match')),
2877 ('f', 'follow', None,
2883 ('f', 'follow', None,
2878 _('follow changeset history, or file history across copies and renames')),
2884 _('follow changeset history, or file history across copies and renames')),
2879 ('i', 'ignore-case', None, _('ignore case when matching')),
2885 ('i', 'ignore-case', None, _('ignore case when matching')),
2880 ('l', 'files-with-matches', None,
2886 ('l', 'files-with-matches', None,
2881 _('print only filenames and revs that match')),
2887 _('print only filenames and revs that match')),
2882 ('n', 'line-number', None, _('print matching line numbers')),
2888 ('n', 'line-number', None, _('print matching line numbers')),
2883 ('r', 'rev', [], _('search in given revision range')),
2889 ('r', 'rev', [], _('search in given revision range')),
2884 ('u', 'user', None, _('print user who committed change')),
2890 ('u', 'user', None, _('print user who committed change')),
2885 ] + walkopts,
2891 ] + walkopts,
2886 _('hg grep [OPTION]... PATTERN [FILE]...')),
2892 _('hg grep [OPTION]... PATTERN [FILE]...')),
2887 "heads":
2893 "heads":
2888 (heads,
2894 (heads,
2889 [('', 'style', '', _('display using template map file')),
2895 [('', 'style', '', _('display using template map file')),
2890 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2896 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2891 ('', 'template', '', _('display with template'))],
2897 ('', 'template', '', _('display with template'))],
2892 _('hg heads [-r REV] [REV]...')),
2898 _('hg heads [-r REV] [REV]...')),
2893 "help": (help_, [], _('hg help [COMMAND]')),
2899 "help": (help_, [], _('hg help [COMMAND]')),
2894 "identify|id":
2900 "identify|id":
2895 (identify,
2901 (identify,
2896 [('r', 'rev', '', _('identify the specified rev')),
2902 [('r', 'rev', '', _('identify the specified rev')),
2897 ('n', 'num', None, _('show local revision number')),
2903 ('n', 'num', None, _('show local revision number')),
2898 ('i', 'id', None, _('show global revision id')),
2904 ('i', 'id', None, _('show global revision id')),
2899 ('b', 'branch', None, _('show branch')),
2905 ('b', 'branch', None, _('show branch')),
2900 ('t', 'tags', None, _('show tags'))],
2906 ('t', 'tags', None, _('show tags'))],
2901 _('hg identify [-nibt] [-r REV] [SOURCE]')),
2907 _('hg identify [-nibt] [-r REV] [SOURCE]')),
2902 "import|patch":
2908 "import|patch":
2903 (import_,
2909 (import_,
2904 [('p', 'strip', 1,
2910 [('p', 'strip', 1,
2905 _('directory strip option for patch. This has the same\n'
2911 _('directory strip option for patch. This has the same\n'
2906 'meaning as the corresponding patch option')),
2912 'meaning as the corresponding patch option')),
2907 ('b', 'base', '', _('base path')),
2913 ('b', 'base', '', _('base path')),
2908 ('f', 'force', None,
2914 ('f', 'force', None,
2909 _('skip check for outstanding uncommitted changes')),
2915 _('skip check for outstanding uncommitted changes')),
2910 ('', 'exact', None,
2916 ('', 'exact', None,
2911 _('apply patch to the nodes from which it was generated')),
2917 _('apply patch to the nodes from which it was generated')),
2912 ('', 'import-branch', None,
2918 ('', 'import-branch', None,
2913 _('Use any branch information in patch (implied by --exact)'))] + commitopts,
2919 _('Use any branch information in patch (implied by --exact)'))] + commitopts,
2914 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2920 _('hg import [-p NUM] [-m MESSAGE] [-f] PATCH...')),
2915 "incoming|in": (incoming,
2921 "incoming|in": (incoming,
2916 [('M', 'no-merges', None, _('do not show merges')),
2922 [('M', 'no-merges', None, _('do not show merges')),
2917 ('f', 'force', None,
2923 ('f', 'force', None,
2918 _('run even when remote repository is unrelated')),
2924 _('run even when remote repository is unrelated')),
2919 ('', 'style', '', _('display using template map file')),
2925 ('', 'style', '', _('display using template map file')),
2920 ('n', 'newest-first', None, _('show newest record first')),
2926 ('n', 'newest-first', None, _('show newest record first')),
2921 ('', 'bundle', '', _('file to store the bundles into')),
2927 ('', 'bundle', '', _('file to store the bundles into')),
2922 ('p', 'patch', None, _('show patch')),
2928 ('p', 'patch', None, _('show patch')),
2923 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2929 ('r', 'rev', [], _('a specific revision up to which you would like to pull')),
2924 ('', 'template', '', _('display with template')),
2930 ('', 'template', '', _('display with template')),
2925 ] + remoteopts,
2931 ] + remoteopts,
2926 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2932 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
2927 ' [--bundle FILENAME] [SOURCE]')),
2933 ' [--bundle FILENAME] [SOURCE]')),
2928 "^init":
2934 "^init":
2929 (init,
2935 (init,
2930 remoteopts,
2936 remoteopts,
2931 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2937 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
2932 "locate":
2938 "locate":
2933 (locate,
2939 (locate,
2934 [('r', 'rev', '', _('search the repository as it stood at rev')),
2940 [('r', 'rev', '', _('search the repository as it stood at rev')),
2935 ('0', 'print0', None,
2941 ('0', 'print0', None,
2936 _('end filenames with NUL, for use with xargs')),
2942 _('end filenames with NUL, for use with xargs')),
2937 ('f', 'fullpath', None,
2943 ('f', 'fullpath', None,
2938 _('print complete paths from the filesystem root')),
2944 _('print complete paths from the filesystem root')),
2939 ] + walkopts,
2945 ] + walkopts,
2940 _('hg locate [OPTION]... [PATTERN]...')),
2946 _('hg locate [OPTION]... [PATTERN]...')),
2941 "^log|history":
2947 "^log|history":
2942 (log,
2948 (log,
2943 [('f', 'follow', None,
2949 [('f', 'follow', None,
2944 _('follow changeset history, or file history across copies and renames')),
2950 _('follow changeset history, or file history across copies and renames')),
2945 ('', 'follow-first', None,
2951 ('', 'follow-first', None,
2946 _('only follow the first parent of merge changesets')),
2952 _('only follow the first parent of merge changesets')),
2947 ('d', 'date', '', _('show revs matching date spec')),
2953 ('d', 'date', '', _('show revs matching date spec')),
2948 ('C', 'copies', None, _('show copied files')),
2954 ('C', 'copies', None, _('show copied files')),
2949 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
2955 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
2950 ('l', 'limit', '', _('limit number of changes displayed')),
2956 ('l', 'limit', '', _('limit number of changes displayed')),
2951 ('r', 'rev', [], _('show the specified revision or range')),
2957 ('r', 'rev', [], _('show the specified revision or range')),
2952 ('', 'removed', None, _('include revs where files were removed')),
2958 ('', 'removed', None, _('include revs where files were removed')),
2953 ('M', 'no-merges', None, _('do not show merges')),
2959 ('M', 'no-merges', None, _('do not show merges')),
2954 ('', 'style', '', _('display using template map file')),
2960 ('', 'style', '', _('display using template map file')),
2955 ('m', 'only-merges', None, _('show only merges')),
2961 ('m', 'only-merges', None, _('show only merges')),
2956 ('p', 'patch', None, _('show patch')),
2962 ('p', 'patch', None, _('show patch')),
2957 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2963 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
2958 ('', 'template', '', _('display with template')),
2964 ('', 'template', '', _('display with template')),
2959 ] + walkopts,
2965 ] + walkopts,
2960 _('hg log [OPTION]... [FILE]')),
2966 _('hg log [OPTION]... [FILE]')),
2961 "manifest": (manifest, [], _('hg manifest [REV]')),
2967 "manifest": (manifest, [], _('hg manifest [REV]')),
2962 "^merge":
2968 "^merge":
2963 (merge,
2969 (merge,
2964 [('f', 'force', None, _('force a merge with outstanding changes')),
2970 [('f', 'force', None, _('force a merge with outstanding changes')),
2965 ('r', 'rev', '', _('revision to merge')),
2971 ('r', 'rev', '', _('revision to merge')),
2966 ],
2972 ],
2967 _('hg merge [-f] [[-r] REV]')),
2973 _('hg merge [-f] [[-r] REV]')),
2968 "outgoing|out": (outgoing,
2974 "outgoing|out": (outgoing,
2969 [('M', 'no-merges', None, _('do not show merges')),
2975 [('M', 'no-merges', None, _('do not show merges')),
2970 ('f', 'force', None,
2976 ('f', 'force', None,
2971 _('run even when remote repository is unrelated')),
2977 _('run even when remote repository is unrelated')),
2972 ('p', 'patch', None, _('show patch')),
2978 ('p', 'patch', None, _('show patch')),
2973 ('', 'style', '', _('display using template map file')),
2979 ('', 'style', '', _('display using template map file')),
2974 ('r', 'rev', [], _('a specific revision you would like to push')),
2980 ('r', 'rev', [], _('a specific revision you would like to push')),
2975 ('n', 'newest-first', None, _('show newest record first')),
2981 ('n', 'newest-first', None, _('show newest record first')),
2976 ('', 'template', '', _('display with template')),
2982 ('', 'template', '', _('display with template')),
2977 ] + remoteopts,
2983 ] + remoteopts,
2978 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2984 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
2979 "^parents":
2985 "^parents":
2980 (parents,
2986 (parents,
2981 [('r', 'rev', '', _('show parents from the specified rev')),
2987 [('r', 'rev', '', _('show parents from the specified rev')),
2982 ('', 'style', '', _('display using template map file')),
2988 ('', 'style', '', _('display using template map file')),
2983 ('', 'template', '', _('display with template'))],
2989 ('', 'template', '', _('display with template'))],
2984 _('hg parents [-r REV] [FILE]')),
2990 _('hg parents [-r REV] [FILE]')),
2985 "paths": (paths, [], _('hg paths [NAME]')),
2991 "paths": (paths, [], _('hg paths [NAME]')),
2986 "^pull":
2992 "^pull":
2987 (pull,
2993 (pull,
2988 [('u', 'update', None,
2994 [('u', 'update', None,
2989 _('update to new tip if changesets were pulled')),
2995 _('update to new tip if changesets were pulled')),
2990 ('f', 'force', None,
2996 ('f', 'force', None,
2991 _('run even when remote repository is unrelated')),
2997 _('run even when remote repository is unrelated')),
2992 ('r', 'rev', [],
2998 ('r', 'rev', [],
2993 _('a specific revision up to which you would like to pull')),
2999 _('a specific revision up to which you would like to pull')),
2994 ] + remoteopts,
3000 ] + remoteopts,
2995 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3001 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
2996 "^push":
3002 "^push":
2997 (push,
3003 (push,
2998 [('f', 'force', None, _('force push')),
3004 [('f', 'force', None, _('force push')),
2999 ('r', 'rev', [], _('a specific revision you would like to push')),
3005 ('r', 'rev', [], _('a specific revision you would like to push')),
3000 ] + remoteopts,
3006 ] + remoteopts,
3001 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3007 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3002 "debugrawcommit|rawcommit":
3008 "debugrawcommit|rawcommit":
3003 (rawcommit,
3009 (rawcommit,
3004 [('p', 'parent', [], _('parent')),
3010 [('p', 'parent', [], _('parent')),
3005 ('d', 'date', '', _('date code')),
3011 ('d', 'date', '', _('date code')),
3006 ('u', 'user', '', _('user')),
3012 ('u', 'user', '', _('user')),
3007 ('F', 'files', '', _('file list'))
3013 ('F', 'files', '', _('file list'))
3008 ] + commitopts,
3014 ] + commitopts,
3009 _('hg debugrawcommit [OPTION]... [FILE]...')),
3015 _('hg debugrawcommit [OPTION]... [FILE]...')),
3010 "recover": (recover, [], _('hg recover')),
3016 "recover": (recover, [], _('hg recover')),
3011 "^remove|rm":
3017 "^remove|rm":
3012 (remove,
3018 (remove,
3013 [('A', 'after', None, _('record remove that has already occurred')),
3019 [('A', 'after', None, _('record remove that has already occurred')),
3014 ('f', 'force', None, _('remove file even if modified')),
3020 ('f', 'force', None, _('remove file even if modified')),
3015 ] + walkopts,
3021 ] + walkopts,
3016 _('hg remove [OPTION]... FILE...')),
3022 _('hg remove [OPTION]... FILE...')),
3017 "rename|mv":
3023 "rename|mv":
3018 (rename,
3024 (rename,
3019 [('A', 'after', None, _('record a rename that has already occurred')),
3025 [('A', 'after', None, _('record a rename that has already occurred')),
3020 ('f', 'force', None,
3026 ('f', 'force', None,
3021 _('forcibly copy over an existing managed file')),
3027 _('forcibly copy over an existing managed file')),
3022 ] + walkopts + dryrunopts,
3028 ] + walkopts + dryrunopts,
3023 _('hg rename [OPTION]... SOURCE... DEST')),
3029 _('hg rename [OPTION]... SOURCE... DEST')),
3024 "^revert":
3030 "^revert":
3025 (revert,
3031 (revert,
3026 [('a', 'all', None, _('revert all changes when no arguments given')),
3032 [('a', 'all', None, _('revert all changes when no arguments given')),
3027 ('d', 'date', '', _('tipmost revision matching date')),
3033 ('d', 'date', '', _('tipmost revision matching date')),
3028 ('r', 'rev', '', _('revision to revert to')),
3034 ('r', 'rev', '', _('revision to revert to')),
3029 ('', 'no-backup', None, _('do not save backup copies of files')),
3035 ('', 'no-backup', None, _('do not save backup copies of files')),
3030 ] + walkopts + dryrunopts,
3036 ] + walkopts + dryrunopts,
3031 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3037 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3032 "rollback": (rollback, [], _('hg rollback')),
3038 "rollback": (rollback, [], _('hg rollback')),
3033 "root": (root, [], _('hg root')),
3039 "root": (root, [], _('hg root')),
3034 "showconfig|debugconfig":
3040 "showconfig|debugconfig":
3035 (showconfig,
3041 (showconfig,
3036 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3042 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3037 _('showconfig [-u] [NAME]...')),
3043 _('showconfig [-u] [NAME]...')),
3038 "^serve":
3044 "^serve":
3039 (serve,
3045 (serve,
3040 [('A', 'accesslog', '', _('name of access log file to write to')),
3046 [('A', 'accesslog', '', _('name of access log file to write to')),
3041 ('d', 'daemon', None, _('run server in background')),
3047 ('d', 'daemon', None, _('run server in background')),
3042 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3048 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3043 ('E', 'errorlog', '', _('name of error log file to write to')),
3049 ('E', 'errorlog', '', _('name of error log file to write to')),
3044 ('p', 'port', 0, _('port to use (default: 8000)')),
3050 ('p', 'port', 0, _('port to use (default: 8000)')),
3045 ('a', 'address', '', _('address to use')),
3051 ('a', 'address', '', _('address to use')),
3046 ('n', 'name', '',
3052 ('n', 'name', '',
3047 _('name to show in web pages (default: working dir)')),
3053 _('name to show in web pages (default: working dir)')),
3048 ('', 'webdir-conf', '', _('name of the webdir config file'
3054 ('', 'webdir-conf', '', _('name of the webdir config file'
3049 ' (serve more than one repo)')),
3055 ' (serve more than one repo)')),
3050 ('', 'pid-file', '', _('name of file to write process ID to')),
3056 ('', 'pid-file', '', _('name of file to write process ID to')),
3051 ('', 'stdio', None, _('for remote clients')),
3057 ('', 'stdio', None, _('for remote clients')),
3052 ('t', 'templates', '', _('web templates to use')),
3058 ('t', 'templates', '', _('web templates to use')),
3053 ('', 'style', '', _('template style to use')),
3059 ('', 'style', '', _('template style to use')),
3054 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3060 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3055 _('hg serve [OPTION]...')),
3061 _('hg serve [OPTION]...')),
3056 "^status|st":
3062 "^status|st":
3057 (status,
3063 (status,
3058 [('A', 'all', None, _('show status of all files')),
3064 [('A', 'all', None, _('show status of all files')),
3059 ('m', 'modified', None, _('show only modified files')),
3065 ('m', 'modified', None, _('show only modified files')),
3060 ('a', 'added', None, _('show only added files')),
3066 ('a', 'added', None, _('show only added files')),
3061 ('r', 'removed', None, _('show only removed files')),
3067 ('r', 'removed', None, _('show only removed files')),
3062 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3068 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3063 ('c', 'clean', None, _('show only files without changes')),
3069 ('c', 'clean', None, _('show only files without changes')),
3064 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3070 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3065 ('i', 'ignored', None, _('show only ignored files')),
3071 ('i', 'ignored', None, _('show only ignored files')),
3066 ('n', 'no-status', None, _('hide status prefix')),
3072 ('n', 'no-status', None, _('hide status prefix')),
3067 ('C', 'copies', None, _('show source of copied files')),
3073 ('C', 'copies', None, _('show source of copied files')),
3068 ('0', 'print0', None,
3074 ('0', 'print0', None,
3069 _('end filenames with NUL, for use with xargs')),
3075 _('end filenames with NUL, for use with xargs')),
3070 ('', 'rev', [], _('show difference from revision')),
3076 ('', 'rev', [], _('show difference from revision')),
3071 ] + walkopts,
3077 ] + walkopts,
3072 _('hg status [OPTION]... [FILE]...')),
3078 _('hg status [OPTION]... [FILE]...')),
3073 "tag":
3079 "tag":
3074 (tag,
3080 (tag,
3075 [('f', 'force', None, _('replace existing tag')),
3081 [('f', 'force', None, _('replace existing tag')),
3076 ('l', 'local', None, _('make the tag local')),
3082 ('l', 'local', None, _('make the tag local')),
3077 ('m', 'message', '', _('message for tag commit log entry')),
3083 ('m', 'message', '', _('message for tag commit log entry')),
3078 ('d', 'date', '', _('record datecode as commit date')),
3084 ('d', 'date', '', _('record datecode as commit date')),
3079 ('u', 'user', '', _('record user as commiter')),
3085 ('u', 'user', '', _('record user as commiter')),
3080 ('r', 'rev', '', _('revision to tag')),
3086 ('r', 'rev', '', _('revision to tag')),
3081 ('', 'remove', None, _('remove a tag'))],
3087 ('', 'remove', None, _('remove a tag'))],
3082 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3088 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3083 "tags": (tags, [], _('hg tags')),
3089 "tags": (tags, [], _('hg tags')),
3084 "tip":
3090 "tip":
3085 (tip,
3091 (tip,
3086 [('', 'style', '', _('display using template map file')),
3092 [('', 'style', '', _('display using template map file')),
3087 ('p', 'patch', None, _('show patch')),
3093 ('p', 'patch', None, _('show patch')),
3088 ('', 'template', '', _('display with template'))],
3094 ('', 'template', '', _('display with template'))],
3089 _('hg tip [-p]')),
3095 _('hg tip [-p]')),
3090 "unbundle":
3096 "unbundle":
3091 (unbundle,
3097 (unbundle,
3092 [('u', 'update', None,
3098 [('u', 'update', None,
3093 _('update to new tip if changesets were unbundled'))],
3099 _('update to new tip if changesets were unbundled'))],
3094 _('hg unbundle [-u] FILE...')),
3100 _('hg unbundle [-u] FILE...')),
3095 "^update|up|checkout|co":
3101 "^update|up|checkout|co":
3096 (update,
3102 (update,
3097 [('C', 'clean', None, _('overwrite locally modified files')),
3103 [('C', 'clean', None, _('overwrite locally modified files')),
3098 ('d', 'date', '', _('tipmost revision matching date')),
3104 ('d', 'date', '', _('tipmost revision matching date')),
3099 ('r', 'rev', '', _('revision'))],
3105 ('r', 'rev', '', _('revision'))],
3100 _('hg update [-C] [-d DATE] [[-r] REV]')),
3106 _('hg update [-C] [-d DATE] [[-r] REV]')),
3101 "verify": (verify, [], _('hg verify')),
3107 "verify": (verify, [], _('hg verify')),
3102 "version": (version_, [], _('hg version')),
3108 "version": (version_, [], _('hg version')),
3103 }
3109 }
3104
3110
3105 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3111 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3106 " debugindex debugindexdot debugdate debuginstall")
3112 " debugindex debugindexdot debugdate debuginstall")
3107 optionalrepo = ("paths serve showconfig")
3113 optionalrepo = ("paths serve showconfig")
3108
3114
3109 def dispatch(args, argv0=None):
3115 def dispatch(args, argv0=None):
3110 try:
3116 try:
3111 u = ui.ui(traceback='--traceback' in args)
3117 u = ui.ui(traceback='--traceback' in args)
3112 except util.Abort, inst:
3118 except util.Abort, inst:
3113 sys.stderr.write(_("abort: %s\n") % inst)
3119 sys.stderr.write(_("abort: %s\n") % inst)
3114 return -1
3120 return -1
3115 return cmdutil.runcatch(u, args, argv0=argv0)
3121 return cmdutil.runcatch(u, args, argv0=argv0)
3116
3122
3117 def run():
3123 def run():
3118 sys.exit(dispatch(sys.argv[1:], argv0=sys.argv[0]))
3124 sys.exit(dispatch(sys.argv[1:], argv0=sys.argv[0]))
@@ -1,1180 +1,1180 b''
1 # hgweb/hgweb_mod.py - Web interface for a repository.
1 # hgweb/hgweb_mod.py - Web interface for a repository.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms
6 # This software may be used and distributed according to the terms
7 # of the GNU General Public License, incorporated herein by reference.
7 # of the GNU General Public License, incorporated herein by reference.
8
8
9 import os, mimetypes, re, zlib, mimetools, cStringIO, sys
9 import os, mimetypes, re, zlib, mimetools, cStringIO, sys
10 import tempfile, urllib, bz2
10 import tempfile, urllib, bz2
11 from mercurial.node import *
11 from mercurial.node import *
12 from mercurial.i18n import gettext as _
12 from mercurial.i18n import gettext as _
13 from mercurial import mdiff, ui, hg, util, archival, streamclone, patch
13 from mercurial import mdiff, ui, hg, util, archival, streamclone, patch
14 from mercurial import revlog, templater
14 from mercurial import revlog, templater
15 from common import get_mtime, staticfile, style_map, paritygen
15 from common import get_mtime, staticfile, style_map, paritygen
16
16
17 def _up(p):
17 def _up(p):
18 if p[0] != "/":
18 if p[0] != "/":
19 p = "/" + p
19 p = "/" + p
20 if p[-1] == "/":
20 if p[-1] == "/":
21 p = p[:-1]
21 p = p[:-1]
22 up = os.path.dirname(p)
22 up = os.path.dirname(p)
23 if up == "/":
23 if up == "/":
24 return "/"
24 return "/"
25 return up + "/"
25 return up + "/"
26
26
27 def revnavgen(pos, pagelen, limit, nodefunc):
27 def revnavgen(pos, pagelen, limit, nodefunc):
28 def seq(factor, limit=None):
28 def seq(factor, limit=None):
29 if limit:
29 if limit:
30 yield limit
30 yield limit
31 if limit >= 20 and limit <= 40:
31 if limit >= 20 and limit <= 40:
32 yield 50
32 yield 50
33 else:
33 else:
34 yield 1 * factor
34 yield 1 * factor
35 yield 3 * factor
35 yield 3 * factor
36 for f in seq(factor * 10):
36 for f in seq(factor * 10):
37 yield f
37 yield f
38
38
39 def nav(**map):
39 def nav(**map):
40 l = []
40 l = []
41 last = 0
41 last = 0
42 for f in seq(1, pagelen):
42 for f in seq(1, pagelen):
43 if f < pagelen or f <= last:
43 if f < pagelen or f <= last:
44 continue
44 continue
45 if f > limit:
45 if f > limit:
46 break
46 break
47 last = f
47 last = f
48 if pos + f < limit:
48 if pos + f < limit:
49 l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
49 l.append(("+%d" % f, hex(nodefunc(pos + f).node())))
50 if pos - f >= 0:
50 if pos - f >= 0:
51 l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
51 l.insert(0, ("-%d" % f, hex(nodefunc(pos - f).node())))
52
52
53 try:
53 try:
54 yield {"label": "(0)", "node": hex(nodefunc('0').node())}
54 yield {"label": "(0)", "node": hex(nodefunc('0').node())}
55
55
56 for label, node in l:
56 for label, node in l:
57 yield {"label": label, "node": node}
57 yield {"label": label, "node": node}
58
58
59 yield {"label": "tip", "node": "tip"}
59 yield {"label": "tip", "node": "tip"}
60 except hg.RepoError:
60 except hg.RepoError:
61 pass
61 pass
62
62
63 return nav
63 return nav
64
64
65 class hgweb(object):
65 class hgweb(object):
66 def __init__(self, repo, name=None):
66 def __init__(self, repo, name=None):
67 if type(repo) == type(""):
67 if type(repo) == type(""):
68 self.repo = hg.repository(ui.ui(report_untrusted=False), repo)
68 self.repo = hg.repository(ui.ui(report_untrusted=False), repo)
69 else:
69 else:
70 self.repo = repo
70 self.repo = repo
71
71
72 self.mtime = -1
72 self.mtime = -1
73 self.reponame = name
73 self.reponame = name
74 self.archives = 'zip', 'gz', 'bz2'
74 self.archives = 'zip', 'gz', 'bz2'
75 self.stripecount = 1
75 self.stripecount = 1
76 # a repo owner may set web.templates in .hg/hgrc to get any file
76 # a repo owner may set web.templates in .hg/hgrc to get any file
77 # readable by the user running the CGI script
77 # readable by the user running the CGI script
78 self.templatepath = self.config("web", "templates",
78 self.templatepath = self.config("web", "templates",
79 templater.templatepath(),
79 templater.templatepath(),
80 untrusted=False)
80 untrusted=False)
81
81
82 # The CGI scripts are often run by a user different from the repo owner.
82 # The CGI scripts are often run by a user different from the repo owner.
83 # Trust the settings from the .hg/hgrc files by default.
83 # Trust the settings from the .hg/hgrc files by default.
84 def config(self, section, name, default=None, untrusted=True):
84 def config(self, section, name, default=None, untrusted=True):
85 return self.repo.ui.config(section, name, default,
85 return self.repo.ui.config(section, name, default,
86 untrusted=untrusted)
86 untrusted=untrusted)
87
87
88 def configbool(self, section, name, default=False, untrusted=True):
88 def configbool(self, section, name, default=False, untrusted=True):
89 return self.repo.ui.configbool(section, name, default,
89 return self.repo.ui.configbool(section, name, default,
90 untrusted=untrusted)
90 untrusted=untrusted)
91
91
92 def configlist(self, section, name, default=None, untrusted=True):
92 def configlist(self, section, name, default=None, untrusted=True):
93 return self.repo.ui.configlist(section, name, default,
93 return self.repo.ui.configlist(section, name, default,
94 untrusted=untrusted)
94 untrusted=untrusted)
95
95
96 def refresh(self):
96 def refresh(self):
97 mtime = get_mtime(self.repo.root)
97 mtime = get_mtime(self.repo.root)
98 if mtime != self.mtime:
98 if mtime != self.mtime:
99 self.mtime = mtime
99 self.mtime = mtime
100 self.repo = hg.repository(self.repo.ui, self.repo.root)
100 self.repo = hg.repository(self.repo.ui, self.repo.root)
101 self.maxchanges = int(self.config("web", "maxchanges", 10))
101 self.maxchanges = int(self.config("web", "maxchanges", 10))
102 self.stripecount = int(self.config("web", "stripes", 1))
102 self.stripecount = int(self.config("web", "stripes", 1))
103 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
103 self.maxshortchanges = int(self.config("web", "maxshortchanges", 60))
104 self.maxfiles = int(self.config("web", "maxfiles", 10))
104 self.maxfiles = int(self.config("web", "maxfiles", 10))
105 self.allowpull = self.configbool("web", "allowpull", True)
105 self.allowpull = self.configbool("web", "allowpull", True)
106 self.encoding = self.config("web", "encoding", util._encoding)
106 self.encoding = self.config("web", "encoding", util._encoding)
107
107
108 def archivelist(self, nodeid):
108 def archivelist(self, nodeid):
109 allowed = self.configlist("web", "allow_archive")
109 allowed = self.configlist("web", "allow_archive")
110 for i, spec in self.archive_specs.iteritems():
110 for i, spec in self.archive_specs.iteritems():
111 if i in allowed or self.configbool("web", "allow" + i):
111 if i in allowed or self.configbool("web", "allow" + i):
112 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
112 yield {"type" : i, "extension" : spec[2], "node" : nodeid}
113
113
114 def listfilediffs(self, files, changeset):
114 def listfilediffs(self, files, changeset):
115 for f in files[:self.maxfiles]:
115 for f in files[:self.maxfiles]:
116 yield self.t("filedifflink", node=hex(changeset), file=f)
116 yield self.t("filedifflink", node=hex(changeset), file=f)
117 if len(files) > self.maxfiles:
117 if len(files) > self.maxfiles:
118 yield self.t("fileellipses")
118 yield self.t("fileellipses")
119
119
120 def siblings(self, siblings=[], hiderev=None, **args):
120 def siblings(self, siblings=[], hiderev=None, **args):
121 siblings = [s for s in siblings if s.node() != nullid]
121 siblings = [s for s in siblings if s.node() != nullid]
122 if len(siblings) == 1 and siblings[0].rev() == hiderev:
122 if len(siblings) == 1 and siblings[0].rev() == hiderev:
123 return
123 return
124 for s in siblings:
124 for s in siblings:
125 d = {'node': hex(s.node()), 'rev': s.rev()}
125 d = {'node': hex(s.node()), 'rev': s.rev()}
126 if hasattr(s, 'path'):
126 if hasattr(s, 'path'):
127 d['file'] = s.path()
127 d['file'] = s.path()
128 d.update(args)
128 d.update(args)
129 yield d
129 yield d
130
130
131 def renamelink(self, fl, node):
131 def renamelink(self, fl, node):
132 r = fl.renamed(node)
132 r = fl.renamed(node)
133 if r:
133 if r:
134 return [dict(file=r[0], node=hex(r[1]))]
134 return [dict(file=r[0], node=hex(r[1]))]
135 return []
135 return []
136
136
137 def nodetagsdict(self, node):
137 def nodetagsdict(self, node):
138 return [{"name": i} for i in self.repo.nodetags(node)]
138 return [{"name": i} for i in self.repo.nodetags(node)]
139
139
140 def nodebranchdict(self, ctx):
140 def nodebranchdict(self, ctx):
141 branches = []
141 branches = []
142 branch = ctx.branch()
142 branch = ctx.branch()
143 if self.repo.branchtags()[branch] == ctx.node():
143 if self.repo.branchtags()[branch] == ctx.node():
144 branches.append({"name": branch})
144 branches.append({"name": branch})
145 return branches
145 return branches
146
146
147 def showtag(self, t1, node=nullid, **args):
147 def showtag(self, t1, node=nullid, **args):
148 for t in self.repo.nodetags(node):
148 for t in self.repo.nodetags(node):
149 yield self.t(t1, tag=t, **args)
149 yield self.t(t1, tag=t, **args)
150
150
151 def diff(self, node1, node2, files):
151 def diff(self, node1, node2, files):
152 def filterfiles(filters, files):
152 def filterfiles(filters, files):
153 l = [x for x in files if x in filters]
153 l = [x for x in files if x in filters]
154
154
155 for t in filters:
155 for t in filters:
156 if t and t[-1] != os.sep:
156 if t and t[-1] != os.sep:
157 t += os.sep
157 t += os.sep
158 l += [x for x in files if x.startswith(t)]
158 l += [x for x in files if x.startswith(t)]
159 return l
159 return l
160
160
161 parity = paritygen(self.stripecount)
161 parity = paritygen(self.stripecount)
162 def diffblock(diff, f, fn):
162 def diffblock(diff, f, fn):
163 yield self.t("diffblock",
163 yield self.t("diffblock",
164 lines=prettyprintlines(diff),
164 lines=prettyprintlines(diff),
165 parity=parity.next(),
165 parity=parity.next(),
166 file=f,
166 file=f,
167 filenode=hex(fn or nullid))
167 filenode=hex(fn or nullid))
168
168
169 def prettyprintlines(diff):
169 def prettyprintlines(diff):
170 for l in diff.splitlines(1):
170 for l in diff.splitlines(1):
171 if l.startswith('+'):
171 if l.startswith('+'):
172 yield self.t("difflineplus", line=l)
172 yield self.t("difflineplus", line=l)
173 elif l.startswith('-'):
173 elif l.startswith('-'):
174 yield self.t("difflineminus", line=l)
174 yield self.t("difflineminus", line=l)
175 elif l.startswith('@'):
175 elif l.startswith('@'):
176 yield self.t("difflineat", line=l)
176 yield self.t("difflineat", line=l)
177 else:
177 else:
178 yield self.t("diffline", line=l)
178 yield self.t("diffline", line=l)
179
179
180 r = self.repo
180 r = self.repo
181 c1 = r.changectx(node1)
181 c1 = r.changectx(node1)
182 c2 = r.changectx(node2)
182 c2 = r.changectx(node2)
183 date1 = util.datestr(c1.date())
183 date1 = util.datestr(c1.date())
184 date2 = util.datestr(c2.date())
184 date2 = util.datestr(c2.date())
185
185
186 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
186 modified, added, removed, deleted, unknown = r.status(node1, node2)[:5]
187 if files:
187 if files:
188 modified, added, removed = map(lambda x: filterfiles(files, x),
188 modified, added, removed = map(lambda x: filterfiles(files, x),
189 (modified, added, removed))
189 (modified, added, removed))
190
190
191 diffopts = patch.diffopts(self.repo.ui, untrusted=True)
191 diffopts = patch.diffopts(self.repo.ui, untrusted=True)
192 for f in modified:
192 for f in modified:
193 to = c1.filectx(f).data()
193 to = c1.filectx(f).data()
194 tn = c2.filectx(f).data()
194 tn = c2.filectx(f).data()
195 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
195 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
196 opts=diffopts), f, tn)
196 opts=diffopts), f, tn)
197 for f in added:
197 for f in added:
198 to = None
198 to = None
199 tn = c2.filectx(f).data()
199 tn = c2.filectx(f).data()
200 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
200 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
201 opts=diffopts), f, tn)
201 opts=diffopts), f, tn)
202 for f in removed:
202 for f in removed:
203 to = c1.filectx(f).data()
203 to = c1.filectx(f).data()
204 tn = None
204 tn = None
205 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
205 yield diffblock(mdiff.unidiff(to, date1, tn, date2, f,
206 opts=diffopts), f, tn)
206 opts=diffopts), f, tn)
207
207
208 def changelog(self, ctx, shortlog=False):
208 def changelog(self, ctx, shortlog=False):
209 def changelist(**map):
209 def changelist(**map):
210 cl = self.repo.changelog
210 cl = self.repo.changelog
211 l = [] # build a list in forward order for efficiency
211 l = [] # build a list in forward order for efficiency
212 for i in xrange(start, end):
212 for i in xrange(start, end):
213 ctx = self.repo.changectx(i)
213 ctx = self.repo.changectx(i)
214 n = ctx.node()
214 n = ctx.node()
215
215
216 l.insert(0, {"parity": parity.next(),
216 l.insert(0, {"parity": parity.next(),
217 "author": ctx.user(),
217 "author": ctx.user(),
218 "parent": self.siblings(ctx.parents(), i - 1),
218 "parent": self.siblings(ctx.parents(), i - 1),
219 "child": self.siblings(ctx.children(), i + 1),
219 "child": self.siblings(ctx.children(), i + 1),
220 "changelogtag": self.showtag("changelogtag",n),
220 "changelogtag": self.showtag("changelogtag",n),
221 "desc": ctx.description(),
221 "desc": ctx.description(),
222 "date": ctx.date(),
222 "date": ctx.date(),
223 "files": self.listfilediffs(ctx.files(), n),
223 "files": self.listfilediffs(ctx.files(), n),
224 "rev": i,
224 "rev": i,
225 "node": hex(n),
225 "node": hex(n),
226 "tags": self.nodetagsdict(n),
226 "tags": self.nodetagsdict(n),
227 "branches": self.nodebranchdict(ctx)})
227 "branches": self.nodebranchdict(ctx)})
228
228
229 for e in l:
229 for e in l:
230 yield e
230 yield e
231
231
232 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
232 maxchanges = shortlog and self.maxshortchanges or self.maxchanges
233 cl = self.repo.changelog
233 cl = self.repo.changelog
234 count = cl.count()
234 count = cl.count()
235 pos = ctx.rev()
235 pos = ctx.rev()
236 start = max(0, pos - maxchanges + 1)
236 start = max(0, pos - maxchanges + 1)
237 end = min(count, start + maxchanges)
237 end = min(count, start + maxchanges)
238 pos = end - 1
238 pos = end - 1
239 parity = paritygen(self.stripecount, offset=start-end)
239 parity = paritygen(self.stripecount, offset=start-end)
240
240
241 changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
241 changenav = revnavgen(pos, maxchanges, count, self.repo.changectx)
242
242
243 yield self.t(shortlog and 'shortlog' or 'changelog',
243 yield self.t(shortlog and 'shortlog' or 'changelog',
244 changenav=changenav,
244 changenav=changenav,
245 node=hex(cl.tip()),
245 node=hex(cl.tip()),
246 rev=pos, changesets=count, entries=changelist,
246 rev=pos, changesets=count, entries=changelist,
247 archives=self.archivelist("tip"))
247 archives=self.archivelist("tip"))
248
248
249 def search(self, query):
249 def search(self, query):
250
250
251 def changelist(**map):
251 def changelist(**map):
252 cl = self.repo.changelog
252 cl = self.repo.changelog
253 count = 0
253 count = 0
254 qw = query.lower().split()
254 qw = query.lower().split()
255
255
256 def revgen():
256 def revgen():
257 for i in xrange(cl.count() - 1, 0, -100):
257 for i in xrange(cl.count() - 1, 0, -100):
258 l = []
258 l = []
259 for j in xrange(max(0, i - 100), i):
259 for j in xrange(max(0, i - 100), i):
260 ctx = self.repo.changectx(j)
260 ctx = self.repo.changectx(j)
261 l.append(ctx)
261 l.append(ctx)
262 l.reverse()
262 l.reverse()
263 for e in l:
263 for e in l:
264 yield e
264 yield e
265
265
266 for ctx in revgen():
266 for ctx in revgen():
267 miss = 0
267 miss = 0
268 for q in qw:
268 for q in qw:
269 if not (q in ctx.user().lower() or
269 if not (q in ctx.user().lower() or
270 q in ctx.description().lower() or
270 q in ctx.description().lower() or
271 q in " ".join(ctx.files()).lower()):
271 q in " ".join(ctx.files()).lower()):
272 miss = 1
272 miss = 1
273 break
273 break
274 if miss:
274 if miss:
275 continue
275 continue
276
276
277 count += 1
277 count += 1
278 n = ctx.node()
278 n = ctx.node()
279
279
280 yield self.t('searchentry',
280 yield self.t('searchentry',
281 parity=parity.next(),
281 parity=parity.next(),
282 author=ctx.user(),
282 author=ctx.user(),
283 parent=self.siblings(ctx.parents()),
283 parent=self.siblings(ctx.parents()),
284 child=self.siblings(ctx.children()),
284 child=self.siblings(ctx.children()),
285 changelogtag=self.showtag("changelogtag",n),
285 changelogtag=self.showtag("changelogtag",n),
286 desc=ctx.description(),
286 desc=ctx.description(),
287 date=ctx.date(),
287 date=ctx.date(),
288 files=self.listfilediffs(ctx.files(), n),
288 files=self.listfilediffs(ctx.files(), n),
289 rev=ctx.rev(),
289 rev=ctx.rev(),
290 node=hex(n),
290 node=hex(n),
291 tags=self.nodetagsdict(n),
291 tags=self.nodetagsdict(n),
292 branches=self.nodebranchdict(ctx))
292 branches=self.nodebranchdict(ctx))
293
293
294 if count >= self.maxchanges:
294 if count >= self.maxchanges:
295 break
295 break
296
296
297 cl = self.repo.changelog
297 cl = self.repo.changelog
298 parity = paritygen(self.stripecount)
298 parity = paritygen(self.stripecount)
299
299
300 yield self.t('search',
300 yield self.t('search',
301 query=query,
301 query=query,
302 node=hex(cl.tip()),
302 node=hex(cl.tip()),
303 entries=changelist,
303 entries=changelist,
304 archives=self.archivelist("tip"))
304 archives=self.archivelist("tip"))
305
305
306 def changeset(self, ctx):
306 def changeset(self, ctx):
307 n = ctx.node()
307 n = ctx.node()
308 parents = ctx.parents()
308 parents = ctx.parents()
309 p1 = parents[0].node()
309 p1 = parents[0].node()
310
310
311 files = []
311 files = []
312 parity = paritygen(self.stripecount)
312 parity = paritygen(self.stripecount)
313 for f in ctx.files():
313 for f in ctx.files():
314 files.append(self.t("filenodelink",
314 files.append(self.t("filenodelink",
315 node=hex(n), file=f,
315 node=hex(n), file=f,
316 parity=parity.next()))
316 parity=parity.next()))
317
317
318 def diff(**map):
318 def diff(**map):
319 yield self.diff(p1, n, None)
319 yield self.diff(p1, n, None)
320
320
321 yield self.t('changeset',
321 yield self.t('changeset',
322 diff=diff,
322 diff=diff,
323 rev=ctx.rev(),
323 rev=ctx.rev(),
324 node=hex(n),
324 node=hex(n),
325 parent=self.siblings(parents),
325 parent=self.siblings(parents),
326 child=self.siblings(ctx.children()),
326 child=self.siblings(ctx.children()),
327 changesettag=self.showtag("changesettag",n),
327 changesettag=self.showtag("changesettag",n),
328 author=ctx.user(),
328 author=ctx.user(),
329 desc=ctx.description(),
329 desc=ctx.description(),
330 date=ctx.date(),
330 date=ctx.date(),
331 files=files,
331 files=files,
332 archives=self.archivelist(hex(n)),
332 archives=self.archivelist(hex(n)),
333 tags=self.nodetagsdict(n),
333 tags=self.nodetagsdict(n),
334 branches=self.nodebranchdict(ctx))
334 branches=self.nodebranchdict(ctx))
335
335
336 def filelog(self, fctx):
336 def filelog(self, fctx):
337 f = fctx.path()
337 f = fctx.path()
338 fl = fctx.filelog()
338 fl = fctx.filelog()
339 count = fl.count()
339 count = fl.count()
340 pagelen = self.maxshortchanges
340 pagelen = self.maxshortchanges
341 pos = fctx.filerev()
341 pos = fctx.filerev()
342 start = max(0, pos - pagelen + 1)
342 start = max(0, pos - pagelen + 1)
343 end = min(count, start + pagelen)
343 end = min(count, start + pagelen)
344 pos = end - 1
344 pos = end - 1
345 parity = paritygen(self.stripecount, offset=start-end)
345 parity = paritygen(self.stripecount, offset=start-end)
346
346
347 def entries(**map):
347 def entries(**map):
348 l = []
348 l = []
349
349
350 for i in xrange(start, end):
350 for i in xrange(start, end):
351 ctx = fctx.filectx(i)
351 ctx = fctx.filectx(i)
352 n = fl.node(i)
352 n = fl.node(i)
353
353
354 l.insert(0, {"parity": parity.next(),
354 l.insert(0, {"parity": parity.next(),
355 "filerev": i,
355 "filerev": i,
356 "file": f,
356 "file": f,
357 "node": hex(ctx.node()),
357 "node": hex(ctx.node()),
358 "author": ctx.user(),
358 "author": ctx.user(),
359 "date": ctx.date(),
359 "date": ctx.date(),
360 "rename": self.renamelink(fl, n),
360 "rename": self.renamelink(fl, n),
361 "parent": self.siblings(fctx.parents()),
361 "parent": self.siblings(fctx.parents()),
362 "child": self.siblings(fctx.children()),
362 "child": self.siblings(fctx.children()),
363 "desc": ctx.description()})
363 "desc": ctx.description()})
364
364
365 for e in l:
365 for e in l:
366 yield e
366 yield e
367
367
368 nodefunc = lambda x: fctx.filectx(fileid=x)
368 nodefunc = lambda x: fctx.filectx(fileid=x)
369 nav = revnavgen(pos, pagelen, count, nodefunc)
369 nav = revnavgen(pos, pagelen, count, nodefunc)
370 yield self.t("filelog", file=f, node=hex(fctx.node()), nav=nav,
370 yield self.t("filelog", file=f, node=hex(fctx.node()), nav=nav,
371 entries=entries)
371 entries=entries)
372
372
373 def filerevision(self, fctx):
373 def filerevision(self, fctx):
374 f = fctx.path()
374 f = fctx.path()
375 text = fctx.data()
375 text = fctx.data()
376 fl = fctx.filelog()
376 fl = fctx.filelog()
377 n = fctx.filenode()
377 n = fctx.filenode()
378 parity = paritygen(self.stripecount)
378 parity = paritygen(self.stripecount)
379
379
380 mt = mimetypes.guess_type(f)[0]
380 mt = mimetypes.guess_type(f)[0]
381 rawtext = text
381 rawtext = text
382 if util.binary(text):
382 if util.binary(text):
383 mt = mt or 'application/octet-stream'
383 mt = mt or 'application/octet-stream'
384 text = "(binary:%s)" % mt
384 text = "(binary:%s)" % mt
385 mt = mt or 'text/plain'
385 mt = mt or 'text/plain'
386
386
387 def lines():
387 def lines():
388 for l, t in enumerate(text.splitlines(1)):
388 for l, t in enumerate(text.splitlines(1)):
389 yield {"line": t,
389 yield {"line": t,
390 "linenumber": "% 6d" % (l + 1),
390 "linenumber": "% 6d" % (l + 1),
391 "parity": parity.next()}
391 "parity": parity.next()}
392
392
393 yield self.t("filerevision",
393 yield self.t("filerevision",
394 file=f,
394 file=f,
395 path=_up(f),
395 path=_up(f),
396 text=lines(),
396 text=lines(),
397 raw=rawtext,
397 raw=rawtext,
398 mimetype=mt,
398 mimetype=mt,
399 rev=fctx.rev(),
399 rev=fctx.rev(),
400 node=hex(fctx.node()),
400 node=hex(fctx.node()),
401 author=fctx.user(),
401 author=fctx.user(),
402 date=fctx.date(),
402 date=fctx.date(),
403 desc=fctx.description(),
403 desc=fctx.description(),
404 parent=self.siblings(fctx.parents()),
404 parent=self.siblings(fctx.parents()),
405 child=self.siblings(fctx.children()),
405 child=self.siblings(fctx.children()),
406 rename=self.renamelink(fl, n),
406 rename=self.renamelink(fl, n),
407 permissions=fctx.manifest().execf(f))
407 permissions=fctx.manifest().flags(f))
408
408
409 def fileannotate(self, fctx):
409 def fileannotate(self, fctx):
410 f = fctx.path()
410 f = fctx.path()
411 n = fctx.filenode()
411 n = fctx.filenode()
412 fl = fctx.filelog()
412 fl = fctx.filelog()
413 parity = paritygen(self.stripecount)
413 parity = paritygen(self.stripecount)
414
414
415 def annotate(**map):
415 def annotate(**map):
416 last = None
416 last = None
417 for f, l in fctx.annotate(follow=True):
417 for f, l in fctx.annotate(follow=True):
418 fnode = f.filenode()
418 fnode = f.filenode()
419 name = self.repo.ui.shortuser(f.user())
419 name = self.repo.ui.shortuser(f.user())
420
420
421 if last != fnode:
421 if last != fnode:
422 last = fnode
422 last = fnode
423
423
424 yield {"parity": parity.next(),
424 yield {"parity": parity.next(),
425 "node": hex(f.node()),
425 "node": hex(f.node()),
426 "rev": f.rev(),
426 "rev": f.rev(),
427 "author": name,
427 "author": name,
428 "file": f.path(),
428 "file": f.path(),
429 "line": l}
429 "line": l}
430
430
431 yield self.t("fileannotate",
431 yield self.t("fileannotate",
432 file=f,
432 file=f,
433 annotate=annotate,
433 annotate=annotate,
434 path=_up(f),
434 path=_up(f),
435 rev=fctx.rev(),
435 rev=fctx.rev(),
436 node=hex(fctx.node()),
436 node=hex(fctx.node()),
437 author=fctx.user(),
437 author=fctx.user(),
438 date=fctx.date(),
438 date=fctx.date(),
439 desc=fctx.description(),
439 desc=fctx.description(),
440 rename=self.renamelink(fl, n),
440 rename=self.renamelink(fl, n),
441 parent=self.siblings(fctx.parents()),
441 parent=self.siblings(fctx.parents()),
442 child=self.siblings(fctx.children()),
442 child=self.siblings(fctx.children()),
443 permissions=fctx.manifest().execf(f))
443 permissions=fctx.manifest().flags(f))
444
444
445 def manifest(self, ctx, path):
445 def manifest(self, ctx, path):
446 mf = ctx.manifest()
446 mf = ctx.manifest()
447 node = ctx.node()
447 node = ctx.node()
448
448
449 files = {}
449 files = {}
450 parity = paritygen(self.stripecount)
450 parity = paritygen(self.stripecount)
451
451
452 if path and path[-1] != "/":
452 if path and path[-1] != "/":
453 path += "/"
453 path += "/"
454 l = len(path)
454 l = len(path)
455 abspath = "/" + path
455 abspath = "/" + path
456
456
457 for f, n in mf.items():
457 for f, n in mf.items():
458 if f[:l] != path:
458 if f[:l] != path:
459 continue
459 continue
460 remain = f[l:]
460 remain = f[l:]
461 if "/" in remain:
461 if "/" in remain:
462 short = remain[:remain.index("/") + 1] # bleah
462 short = remain[:remain.index("/") + 1] # bleah
463 files[short] = (f, None)
463 files[short] = (f, None)
464 else:
464 else:
465 short = os.path.basename(remain)
465 short = os.path.basename(remain)
466 files[short] = (f, n)
466 files[short] = (f, n)
467
467
468 def filelist(**map):
468 def filelist(**map):
469 fl = files.keys()
469 fl = files.keys()
470 fl.sort()
470 fl.sort()
471 for f in fl:
471 for f in fl:
472 full, fnode = files[f]
472 full, fnode = files[f]
473 if not fnode:
473 if not fnode:
474 continue
474 continue
475
475
476 yield {"file": full,
476 yield {"file": full,
477 "parity": parity.next(),
477 "parity": parity.next(),
478 "basename": f,
478 "basename": f,
479 "size": ctx.filectx(full).size(),
479 "size": ctx.filectx(full).size(),
480 "permissions": mf.execf(full)}
480 "permissions": mf.flags(full)}
481
481
482 def dirlist(**map):
482 def dirlist(**map):
483 fl = files.keys()
483 fl = files.keys()
484 fl.sort()
484 fl.sort()
485 for f in fl:
485 for f in fl:
486 full, fnode = files[f]
486 full, fnode = files[f]
487 if fnode:
487 if fnode:
488 continue
488 continue
489
489
490 yield {"parity": parity.next(),
490 yield {"parity": parity.next(),
491 "path": os.path.join(abspath, f),
491 "path": os.path.join(abspath, f),
492 "basename": f[:-1]}
492 "basename": f[:-1]}
493
493
494 yield self.t("manifest",
494 yield self.t("manifest",
495 rev=ctx.rev(),
495 rev=ctx.rev(),
496 node=hex(node),
496 node=hex(node),
497 path=abspath,
497 path=abspath,
498 up=_up(abspath),
498 up=_up(abspath),
499 upparity=parity.next(),
499 upparity=parity.next(),
500 fentries=filelist,
500 fentries=filelist,
501 dentries=dirlist,
501 dentries=dirlist,
502 archives=self.archivelist(hex(node)),
502 archives=self.archivelist(hex(node)),
503 tags=self.nodetagsdict(node),
503 tags=self.nodetagsdict(node),
504 branches=self.nodebranchdict(ctx))
504 branches=self.nodebranchdict(ctx))
505
505
506 def tags(self):
506 def tags(self):
507 i = self.repo.tagslist()
507 i = self.repo.tagslist()
508 i.reverse()
508 i.reverse()
509 parity = paritygen(self.stripecount)
509 parity = paritygen(self.stripecount)
510
510
511 def entries(notip=False, **map):
511 def entries(notip=False, **map):
512 for k, n in i:
512 for k, n in i:
513 if notip and k == "tip":
513 if notip and k == "tip":
514 continue
514 continue
515 yield {"parity": parity.next(),
515 yield {"parity": parity.next(),
516 "tag": k,
516 "tag": k,
517 "date": self.repo.changectx(n).date(),
517 "date": self.repo.changectx(n).date(),
518 "node": hex(n)}
518 "node": hex(n)}
519
519
520 yield self.t("tags",
520 yield self.t("tags",
521 node=hex(self.repo.changelog.tip()),
521 node=hex(self.repo.changelog.tip()),
522 entries=lambda **x: entries(False, **x),
522 entries=lambda **x: entries(False, **x),
523 entriesnotip=lambda **x: entries(True, **x))
523 entriesnotip=lambda **x: entries(True, **x))
524
524
525 def summary(self):
525 def summary(self):
526 i = self.repo.tagslist()
526 i = self.repo.tagslist()
527 i.reverse()
527 i.reverse()
528
528
529 def tagentries(**map):
529 def tagentries(**map):
530 parity = paritygen(self.stripecount)
530 parity = paritygen(self.stripecount)
531 count = 0
531 count = 0
532 for k, n in i:
532 for k, n in i:
533 if k == "tip": # skip tip
533 if k == "tip": # skip tip
534 continue;
534 continue;
535
535
536 count += 1
536 count += 1
537 if count > 10: # limit to 10 tags
537 if count > 10: # limit to 10 tags
538 break;
538 break;
539
539
540 yield self.t("tagentry",
540 yield self.t("tagentry",
541 parity=parity.next(),
541 parity=parity.next(),
542 tag=k,
542 tag=k,
543 node=hex(n),
543 node=hex(n),
544 date=self.repo.changectx(n).date())
544 date=self.repo.changectx(n).date())
545
545
546
546
547 def branches(**map):
547 def branches(**map):
548 parity = paritygen(self.stripecount)
548 parity = paritygen(self.stripecount)
549
549
550 b = self.repo.branchtags()
550 b = self.repo.branchtags()
551 l = [(-self.repo.changelog.rev(n), n, t) for t, n in b.items()]
551 l = [(-self.repo.changelog.rev(n), n, t) for t, n in b.items()]
552 l.sort()
552 l.sort()
553
553
554 for r,n,t in l:
554 for r,n,t in l:
555 ctx = self.repo.changectx(n)
555 ctx = self.repo.changectx(n)
556
556
557 yield {'parity': parity.next(),
557 yield {'parity': parity.next(),
558 'branch': t,
558 'branch': t,
559 'node': hex(n),
559 'node': hex(n),
560 'date': ctx.date()}
560 'date': ctx.date()}
561
561
562 def changelist(**map):
562 def changelist(**map):
563 parity = paritygen(self.stripecount, offset=start-end)
563 parity = paritygen(self.stripecount, offset=start-end)
564 l = [] # build a list in forward order for efficiency
564 l = [] # build a list in forward order for efficiency
565 for i in xrange(start, end):
565 for i in xrange(start, end):
566 ctx = self.repo.changectx(i)
566 ctx = self.repo.changectx(i)
567 n = ctx.node()
567 n = ctx.node()
568 hn = hex(n)
568 hn = hex(n)
569
569
570 l.insert(0, self.t(
570 l.insert(0, self.t(
571 'shortlogentry',
571 'shortlogentry',
572 parity=parity.next(),
572 parity=parity.next(),
573 author=ctx.user(),
573 author=ctx.user(),
574 desc=ctx.description(),
574 desc=ctx.description(),
575 date=ctx.date(),
575 date=ctx.date(),
576 rev=i,
576 rev=i,
577 node=hn,
577 node=hn,
578 tags=self.nodetagsdict(n),
578 tags=self.nodetagsdict(n),
579 branches=self.nodebranchdict(ctx)))
579 branches=self.nodebranchdict(ctx)))
580
580
581 yield l
581 yield l
582
582
583 cl = self.repo.changelog
583 cl = self.repo.changelog
584 count = cl.count()
584 count = cl.count()
585 start = max(0, count - self.maxchanges)
585 start = max(0, count - self.maxchanges)
586 end = min(count, start + self.maxchanges)
586 end = min(count, start + self.maxchanges)
587
587
588 yield self.t("summary",
588 yield self.t("summary",
589 desc=self.config("web", "description", "unknown"),
589 desc=self.config("web", "description", "unknown"),
590 owner=(self.config("ui", "username") or # preferred
590 owner=(self.config("ui", "username") or # preferred
591 self.config("web", "contact") or # deprecated
591 self.config("web", "contact") or # deprecated
592 self.config("web", "author", "unknown")), # also
592 self.config("web", "author", "unknown")), # also
593 lastchange=cl.read(cl.tip())[2],
593 lastchange=cl.read(cl.tip())[2],
594 tags=tagentries,
594 tags=tagentries,
595 branches=branches,
595 branches=branches,
596 shortlog=changelist,
596 shortlog=changelist,
597 node=hex(cl.tip()),
597 node=hex(cl.tip()),
598 archives=self.archivelist("tip"))
598 archives=self.archivelist("tip"))
599
599
600 def filediff(self, fctx):
600 def filediff(self, fctx):
601 n = fctx.node()
601 n = fctx.node()
602 path = fctx.path()
602 path = fctx.path()
603 parents = fctx.parents()
603 parents = fctx.parents()
604 p1 = parents and parents[0].node() or nullid
604 p1 = parents and parents[0].node() or nullid
605
605
606 def diff(**map):
606 def diff(**map):
607 yield self.diff(p1, n, [path])
607 yield self.diff(p1, n, [path])
608
608
609 yield self.t("filediff",
609 yield self.t("filediff",
610 file=path,
610 file=path,
611 node=hex(n),
611 node=hex(n),
612 rev=fctx.rev(),
612 rev=fctx.rev(),
613 parent=self.siblings(parents),
613 parent=self.siblings(parents),
614 child=self.siblings(fctx.children()),
614 child=self.siblings(fctx.children()),
615 diff=diff)
615 diff=diff)
616
616
617 archive_specs = {
617 archive_specs = {
618 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
618 'bz2': ('application/x-tar', 'tbz2', '.tar.bz2', None),
619 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
619 'gz': ('application/x-tar', 'tgz', '.tar.gz', None),
620 'zip': ('application/zip', 'zip', '.zip', None),
620 'zip': ('application/zip', 'zip', '.zip', None),
621 }
621 }
622
622
623 def archive(self, req, key, type_):
623 def archive(self, req, key, type_):
624 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
624 reponame = re.sub(r"\W+", "-", os.path.basename(self.reponame))
625 cnode = self.repo.lookup(key)
625 cnode = self.repo.lookup(key)
626 arch_version = key
626 arch_version = key
627 if cnode == key or key == 'tip':
627 if cnode == key or key == 'tip':
628 arch_version = short(cnode)
628 arch_version = short(cnode)
629 name = "%s-%s" % (reponame, arch_version)
629 name = "%s-%s" % (reponame, arch_version)
630 mimetype, artype, extension, encoding = self.archive_specs[type_]
630 mimetype, artype, extension, encoding = self.archive_specs[type_]
631 headers = [('Content-type', mimetype),
631 headers = [('Content-type', mimetype),
632 ('Content-disposition', 'attachment; filename=%s%s' %
632 ('Content-disposition', 'attachment; filename=%s%s' %
633 (name, extension))]
633 (name, extension))]
634 if encoding:
634 if encoding:
635 headers.append(('Content-encoding', encoding))
635 headers.append(('Content-encoding', encoding))
636 req.header(headers)
636 req.header(headers)
637 archival.archive(self.repo, req.out, cnode, artype, prefix=name)
637 archival.archive(self.repo, req.out, cnode, artype, prefix=name)
638
638
639 # add tags to things
639 # add tags to things
640 # tags -> list of changesets corresponding to tags
640 # tags -> list of changesets corresponding to tags
641 # find tag, changeset, file
641 # find tag, changeset, file
642
642
643 def cleanpath(self, path):
643 def cleanpath(self, path):
644 path = path.lstrip('/')
644 path = path.lstrip('/')
645 return util.canonpath(self.repo.root, '', path)
645 return util.canonpath(self.repo.root, '', path)
646
646
647 def run(self):
647 def run(self):
648 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
648 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
649 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
649 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
650 import mercurial.hgweb.wsgicgi as wsgicgi
650 import mercurial.hgweb.wsgicgi as wsgicgi
651 from request import wsgiapplication
651 from request import wsgiapplication
652 def make_web_app():
652 def make_web_app():
653 return self
653 return self
654 wsgicgi.launch(wsgiapplication(make_web_app))
654 wsgicgi.launch(wsgiapplication(make_web_app))
655
655
656 def run_wsgi(self, req):
656 def run_wsgi(self, req):
657 def header(**map):
657 def header(**map):
658 header_file = cStringIO.StringIO(
658 header_file = cStringIO.StringIO(
659 ''.join(self.t("header", encoding=self.encoding, **map)))
659 ''.join(self.t("header", encoding=self.encoding, **map)))
660 msg = mimetools.Message(header_file, 0)
660 msg = mimetools.Message(header_file, 0)
661 req.header(msg.items())
661 req.header(msg.items())
662 yield header_file.read()
662 yield header_file.read()
663
663
664 def rawfileheader(**map):
664 def rawfileheader(**map):
665 req.header([('Content-type', map['mimetype']),
665 req.header([('Content-type', map['mimetype']),
666 ('Content-disposition', 'filename=%s' % map['file']),
666 ('Content-disposition', 'filename=%s' % map['file']),
667 ('Content-length', str(len(map['raw'])))])
667 ('Content-length', str(len(map['raw'])))])
668 yield ''
668 yield ''
669
669
670 def footer(**map):
670 def footer(**map):
671 yield self.t("footer", **map)
671 yield self.t("footer", **map)
672
672
673 def motd(**map):
673 def motd(**map):
674 yield self.config("web", "motd", "")
674 yield self.config("web", "motd", "")
675
675
676 def expand_form(form):
676 def expand_form(form):
677 shortcuts = {
677 shortcuts = {
678 'cl': [('cmd', ['changelog']), ('rev', None)],
678 'cl': [('cmd', ['changelog']), ('rev', None)],
679 'sl': [('cmd', ['shortlog']), ('rev', None)],
679 'sl': [('cmd', ['shortlog']), ('rev', None)],
680 'cs': [('cmd', ['changeset']), ('node', None)],
680 'cs': [('cmd', ['changeset']), ('node', None)],
681 'f': [('cmd', ['file']), ('filenode', None)],
681 'f': [('cmd', ['file']), ('filenode', None)],
682 'fl': [('cmd', ['filelog']), ('filenode', None)],
682 'fl': [('cmd', ['filelog']), ('filenode', None)],
683 'fd': [('cmd', ['filediff']), ('node', None)],
683 'fd': [('cmd', ['filediff']), ('node', None)],
684 'fa': [('cmd', ['annotate']), ('filenode', None)],
684 'fa': [('cmd', ['annotate']), ('filenode', None)],
685 'mf': [('cmd', ['manifest']), ('manifest', None)],
685 'mf': [('cmd', ['manifest']), ('manifest', None)],
686 'ca': [('cmd', ['archive']), ('node', None)],
686 'ca': [('cmd', ['archive']), ('node', None)],
687 'tags': [('cmd', ['tags'])],
687 'tags': [('cmd', ['tags'])],
688 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
688 'tip': [('cmd', ['changeset']), ('node', ['tip'])],
689 'static': [('cmd', ['static']), ('file', None)]
689 'static': [('cmd', ['static']), ('file', None)]
690 }
690 }
691
691
692 for k in shortcuts.iterkeys():
692 for k in shortcuts.iterkeys():
693 if form.has_key(k):
693 if form.has_key(k):
694 for name, value in shortcuts[k]:
694 for name, value in shortcuts[k]:
695 if value is None:
695 if value is None:
696 value = form[k]
696 value = form[k]
697 form[name] = value
697 form[name] = value
698 del form[k]
698 del form[k]
699
699
700 def rewrite_request(req):
700 def rewrite_request(req):
701 '''translate new web interface to traditional format'''
701 '''translate new web interface to traditional format'''
702
702
703 def spliturl(req):
703 def spliturl(req):
704 def firstitem(query):
704 def firstitem(query):
705 return query.split('&', 1)[0].split(';', 1)[0]
705 return query.split('&', 1)[0].split(';', 1)[0]
706
706
707 def normurl(url):
707 def normurl(url):
708 inner = '/'.join([x for x in url.split('/') if x])
708 inner = '/'.join([x for x in url.split('/') if x])
709 tl = len(url) > 1 and url.endswith('/') and '/' or ''
709 tl = len(url) > 1 and url.endswith('/') and '/' or ''
710
710
711 return '%s%s%s' % (url.startswith('/') and '/' or '',
711 return '%s%s%s' % (url.startswith('/') and '/' or '',
712 inner, tl)
712 inner, tl)
713
713
714 root = normurl(urllib.unquote(req.env.get('REQUEST_URI', '').split('?', 1)[0]))
714 root = normurl(urllib.unquote(req.env.get('REQUEST_URI', '').split('?', 1)[0]))
715 pi = normurl(req.env.get('PATH_INFO', ''))
715 pi = normurl(req.env.get('PATH_INFO', ''))
716 if pi:
716 if pi:
717 # strip leading /
717 # strip leading /
718 pi = pi[1:]
718 pi = pi[1:]
719 if pi:
719 if pi:
720 root = root[:root.rfind(pi)]
720 root = root[:root.rfind(pi)]
721 if req.env.has_key('REPO_NAME'):
721 if req.env.has_key('REPO_NAME'):
722 rn = req.env['REPO_NAME'] + '/'
722 rn = req.env['REPO_NAME'] + '/'
723 root += rn
723 root += rn
724 query = pi[len(rn):]
724 query = pi[len(rn):]
725 else:
725 else:
726 query = pi
726 query = pi
727 else:
727 else:
728 root += '?'
728 root += '?'
729 query = firstitem(req.env['QUERY_STRING'])
729 query = firstitem(req.env['QUERY_STRING'])
730
730
731 return (root, query)
731 return (root, query)
732
732
733 req.url, query = spliturl(req)
733 req.url, query = spliturl(req)
734
734
735 if req.form.has_key('cmd'):
735 if req.form.has_key('cmd'):
736 # old style
736 # old style
737 return
737 return
738
738
739 args = query.split('/', 2)
739 args = query.split('/', 2)
740 if not args or not args[0]:
740 if not args or not args[0]:
741 return
741 return
742
742
743 cmd = args.pop(0)
743 cmd = args.pop(0)
744 style = cmd.rfind('-')
744 style = cmd.rfind('-')
745 if style != -1:
745 if style != -1:
746 req.form['style'] = [cmd[:style]]
746 req.form['style'] = [cmd[:style]]
747 cmd = cmd[style+1:]
747 cmd = cmd[style+1:]
748 # avoid accepting e.g. style parameter as command
748 # avoid accepting e.g. style parameter as command
749 if hasattr(self, 'do_' + cmd):
749 if hasattr(self, 'do_' + cmd):
750 req.form['cmd'] = [cmd]
750 req.form['cmd'] = [cmd]
751
751
752 if args and args[0]:
752 if args and args[0]:
753 node = args.pop(0)
753 node = args.pop(0)
754 req.form['node'] = [node]
754 req.form['node'] = [node]
755 if args:
755 if args:
756 req.form['file'] = args
756 req.form['file'] = args
757
757
758 if cmd == 'static':
758 if cmd == 'static':
759 req.form['file'] = req.form['node']
759 req.form['file'] = req.form['node']
760 elif cmd == 'archive':
760 elif cmd == 'archive':
761 fn = req.form['node'][0]
761 fn = req.form['node'][0]
762 for type_, spec in self.archive_specs.iteritems():
762 for type_, spec in self.archive_specs.iteritems():
763 ext = spec[2]
763 ext = spec[2]
764 if fn.endswith(ext):
764 if fn.endswith(ext):
765 req.form['node'] = [fn[:-len(ext)]]
765 req.form['node'] = [fn[:-len(ext)]]
766 req.form['type'] = [type_]
766 req.form['type'] = [type_]
767
767
768 def sessionvars(**map):
768 def sessionvars(**map):
769 fields = []
769 fields = []
770 if req.form.has_key('style'):
770 if req.form.has_key('style'):
771 style = req.form['style'][0]
771 style = req.form['style'][0]
772 if style != self.config('web', 'style', ''):
772 if style != self.config('web', 'style', ''):
773 fields.append(('style', style))
773 fields.append(('style', style))
774
774
775 separator = req.url[-1] == '?' and ';' or '?'
775 separator = req.url[-1] == '?' and ';' or '?'
776 for name, value in fields:
776 for name, value in fields:
777 yield dict(name=name, value=value, separator=separator)
777 yield dict(name=name, value=value, separator=separator)
778 separator = ';'
778 separator = ';'
779
779
780 self.refresh()
780 self.refresh()
781
781
782 expand_form(req.form)
782 expand_form(req.form)
783 rewrite_request(req)
783 rewrite_request(req)
784
784
785 style = self.config("web", "style", "")
785 style = self.config("web", "style", "")
786 if req.form.has_key('style'):
786 if req.form.has_key('style'):
787 style = req.form['style'][0]
787 style = req.form['style'][0]
788 mapfile = style_map(self.templatepath, style)
788 mapfile = style_map(self.templatepath, style)
789
789
790 port = req.env["SERVER_PORT"]
790 port = req.env["SERVER_PORT"]
791 port = port != "80" and (":" + port) or ""
791 port = port != "80" and (":" + port) or ""
792 urlbase = 'http://%s%s' % (req.env['SERVER_NAME'], port)
792 urlbase = 'http://%s%s' % (req.env['SERVER_NAME'], port)
793 staticurl = self.config("web", "staticurl") or req.url + 'static/'
793 staticurl = self.config("web", "staticurl") or req.url + 'static/'
794 if not staticurl.endswith('/'):
794 if not staticurl.endswith('/'):
795 staticurl += '/'
795 staticurl += '/'
796
796
797 if not self.reponame:
797 if not self.reponame:
798 self.reponame = (self.config("web", "name")
798 self.reponame = (self.config("web", "name")
799 or req.env.get('REPO_NAME')
799 or req.env.get('REPO_NAME')
800 or req.url.strip('/') or self.repo.root)
800 or req.url.strip('/') or self.repo.root)
801
801
802 self.t = templater.templater(mapfile, templater.common_filters,
802 self.t = templater.templater(mapfile, templater.common_filters,
803 defaults={"url": req.url,
803 defaults={"url": req.url,
804 "staticurl": staticurl,
804 "staticurl": staticurl,
805 "urlbase": urlbase,
805 "urlbase": urlbase,
806 "repo": self.reponame,
806 "repo": self.reponame,
807 "header": header,
807 "header": header,
808 "footer": footer,
808 "footer": footer,
809 "motd": motd,
809 "motd": motd,
810 "rawfileheader": rawfileheader,
810 "rawfileheader": rawfileheader,
811 "sessionvars": sessionvars
811 "sessionvars": sessionvars
812 })
812 })
813
813
814 try:
814 try:
815 if not req.form.has_key('cmd'):
815 if not req.form.has_key('cmd'):
816 req.form['cmd'] = [self.t.cache['default']]
816 req.form['cmd'] = [self.t.cache['default']]
817
817
818 cmd = req.form['cmd'][0]
818 cmd = req.form['cmd'][0]
819
819
820 method = getattr(self, 'do_' + cmd, None)
820 method = getattr(self, 'do_' + cmd, None)
821 if method:
821 if method:
822 try:
822 try:
823 method(req)
823 method(req)
824 except (hg.RepoError, revlog.RevlogError), inst:
824 except (hg.RepoError, revlog.RevlogError), inst:
825 req.write(self.t("error", error=str(inst)))
825 req.write(self.t("error", error=str(inst)))
826 else:
826 else:
827 req.write(self.t("error", error='No such method: ' + cmd))
827 req.write(self.t("error", error='No such method: ' + cmd))
828 finally:
828 finally:
829 self.t = None
829 self.t = None
830
830
831 def changectx(self, req):
831 def changectx(self, req):
832 if req.form.has_key('node'):
832 if req.form.has_key('node'):
833 changeid = req.form['node'][0]
833 changeid = req.form['node'][0]
834 elif req.form.has_key('manifest'):
834 elif req.form.has_key('manifest'):
835 changeid = req.form['manifest'][0]
835 changeid = req.form['manifest'][0]
836 else:
836 else:
837 changeid = self.repo.changelog.count() - 1
837 changeid = self.repo.changelog.count() - 1
838
838
839 try:
839 try:
840 ctx = self.repo.changectx(changeid)
840 ctx = self.repo.changectx(changeid)
841 except hg.RepoError:
841 except hg.RepoError:
842 man = self.repo.manifest
842 man = self.repo.manifest
843 mn = man.lookup(changeid)
843 mn = man.lookup(changeid)
844 ctx = self.repo.changectx(man.linkrev(mn))
844 ctx = self.repo.changectx(man.linkrev(mn))
845
845
846 return ctx
846 return ctx
847
847
848 def filectx(self, req):
848 def filectx(self, req):
849 path = self.cleanpath(req.form['file'][0])
849 path = self.cleanpath(req.form['file'][0])
850 if req.form.has_key('node'):
850 if req.form.has_key('node'):
851 changeid = req.form['node'][0]
851 changeid = req.form['node'][0]
852 else:
852 else:
853 changeid = req.form['filenode'][0]
853 changeid = req.form['filenode'][0]
854 try:
854 try:
855 ctx = self.repo.changectx(changeid)
855 ctx = self.repo.changectx(changeid)
856 fctx = ctx.filectx(path)
856 fctx = ctx.filectx(path)
857 except hg.RepoError:
857 except hg.RepoError:
858 fctx = self.repo.filectx(path, fileid=changeid)
858 fctx = self.repo.filectx(path, fileid=changeid)
859
859
860 return fctx
860 return fctx
861
861
862 def do_log(self, req):
862 def do_log(self, req):
863 if req.form.has_key('file') and req.form['file'][0]:
863 if req.form.has_key('file') and req.form['file'][0]:
864 self.do_filelog(req)
864 self.do_filelog(req)
865 else:
865 else:
866 self.do_changelog(req)
866 self.do_changelog(req)
867
867
868 def do_rev(self, req):
868 def do_rev(self, req):
869 self.do_changeset(req)
869 self.do_changeset(req)
870
870
871 def do_file(self, req):
871 def do_file(self, req):
872 path = self.cleanpath(req.form.get('file', [''])[0])
872 path = self.cleanpath(req.form.get('file', [''])[0])
873 if path:
873 if path:
874 try:
874 try:
875 req.write(self.filerevision(self.filectx(req)))
875 req.write(self.filerevision(self.filectx(req)))
876 return
876 return
877 except revlog.LookupError:
877 except revlog.LookupError:
878 pass
878 pass
879
879
880 req.write(self.manifest(self.changectx(req), path))
880 req.write(self.manifest(self.changectx(req), path))
881
881
882 def do_diff(self, req):
882 def do_diff(self, req):
883 self.do_filediff(req)
883 self.do_filediff(req)
884
884
885 def do_changelog(self, req, shortlog = False):
885 def do_changelog(self, req, shortlog = False):
886 if req.form.has_key('node'):
886 if req.form.has_key('node'):
887 ctx = self.changectx(req)
887 ctx = self.changectx(req)
888 else:
888 else:
889 if req.form.has_key('rev'):
889 if req.form.has_key('rev'):
890 hi = req.form['rev'][0]
890 hi = req.form['rev'][0]
891 else:
891 else:
892 hi = self.repo.changelog.count() - 1
892 hi = self.repo.changelog.count() - 1
893 try:
893 try:
894 ctx = self.repo.changectx(hi)
894 ctx = self.repo.changectx(hi)
895 except hg.RepoError:
895 except hg.RepoError:
896 req.write(self.search(hi)) # XXX redirect to 404 page?
896 req.write(self.search(hi)) # XXX redirect to 404 page?
897 return
897 return
898
898
899 req.write(self.changelog(ctx, shortlog = shortlog))
899 req.write(self.changelog(ctx, shortlog = shortlog))
900
900
901 def do_shortlog(self, req):
901 def do_shortlog(self, req):
902 self.do_changelog(req, shortlog = True)
902 self.do_changelog(req, shortlog = True)
903
903
904 def do_changeset(self, req):
904 def do_changeset(self, req):
905 req.write(self.changeset(self.changectx(req)))
905 req.write(self.changeset(self.changectx(req)))
906
906
907 def do_manifest(self, req):
907 def do_manifest(self, req):
908 req.write(self.manifest(self.changectx(req),
908 req.write(self.manifest(self.changectx(req),
909 self.cleanpath(req.form['path'][0])))
909 self.cleanpath(req.form['path'][0])))
910
910
911 def do_tags(self, req):
911 def do_tags(self, req):
912 req.write(self.tags())
912 req.write(self.tags())
913
913
914 def do_summary(self, req):
914 def do_summary(self, req):
915 req.write(self.summary())
915 req.write(self.summary())
916
916
917 def do_filediff(self, req):
917 def do_filediff(self, req):
918 req.write(self.filediff(self.filectx(req)))
918 req.write(self.filediff(self.filectx(req)))
919
919
920 def do_annotate(self, req):
920 def do_annotate(self, req):
921 req.write(self.fileannotate(self.filectx(req)))
921 req.write(self.fileannotate(self.filectx(req)))
922
922
923 def do_filelog(self, req):
923 def do_filelog(self, req):
924 req.write(self.filelog(self.filectx(req)))
924 req.write(self.filelog(self.filectx(req)))
925
925
926 def do_lookup(self, req):
926 def do_lookup(self, req):
927 try:
927 try:
928 r = hex(self.repo.lookup(req.form['key'][0]))
928 r = hex(self.repo.lookup(req.form['key'][0]))
929 success = 1
929 success = 1
930 except Exception,inst:
930 except Exception,inst:
931 r = str(inst)
931 r = str(inst)
932 success = 0
932 success = 0
933 resp = "%s %s\n" % (success, r)
933 resp = "%s %s\n" % (success, r)
934 req.httphdr("application/mercurial-0.1", length=len(resp))
934 req.httphdr("application/mercurial-0.1", length=len(resp))
935 req.write(resp)
935 req.write(resp)
936
936
937 def do_heads(self, req):
937 def do_heads(self, req):
938 resp = " ".join(map(hex, self.repo.heads())) + "\n"
938 resp = " ".join(map(hex, self.repo.heads())) + "\n"
939 req.httphdr("application/mercurial-0.1", length=len(resp))
939 req.httphdr("application/mercurial-0.1", length=len(resp))
940 req.write(resp)
940 req.write(resp)
941
941
942 def do_branches(self, req):
942 def do_branches(self, req):
943 nodes = []
943 nodes = []
944 if req.form.has_key('nodes'):
944 if req.form.has_key('nodes'):
945 nodes = map(bin, req.form['nodes'][0].split(" "))
945 nodes = map(bin, req.form['nodes'][0].split(" "))
946 resp = cStringIO.StringIO()
946 resp = cStringIO.StringIO()
947 for b in self.repo.branches(nodes):
947 for b in self.repo.branches(nodes):
948 resp.write(" ".join(map(hex, b)) + "\n")
948 resp.write(" ".join(map(hex, b)) + "\n")
949 resp = resp.getvalue()
949 resp = resp.getvalue()
950 req.httphdr("application/mercurial-0.1", length=len(resp))
950 req.httphdr("application/mercurial-0.1", length=len(resp))
951 req.write(resp)
951 req.write(resp)
952
952
953 def do_between(self, req):
953 def do_between(self, req):
954 if req.form.has_key('pairs'):
954 if req.form.has_key('pairs'):
955 pairs = [map(bin, p.split("-"))
955 pairs = [map(bin, p.split("-"))
956 for p in req.form['pairs'][0].split(" ")]
956 for p in req.form['pairs'][0].split(" ")]
957 resp = cStringIO.StringIO()
957 resp = cStringIO.StringIO()
958 for b in self.repo.between(pairs):
958 for b in self.repo.between(pairs):
959 resp.write(" ".join(map(hex, b)) + "\n")
959 resp.write(" ".join(map(hex, b)) + "\n")
960 resp = resp.getvalue()
960 resp = resp.getvalue()
961 req.httphdr("application/mercurial-0.1", length=len(resp))
961 req.httphdr("application/mercurial-0.1", length=len(resp))
962 req.write(resp)
962 req.write(resp)
963
963
964 def do_changegroup(self, req):
964 def do_changegroup(self, req):
965 req.httphdr("application/mercurial-0.1")
965 req.httphdr("application/mercurial-0.1")
966 nodes = []
966 nodes = []
967 if not self.allowpull:
967 if not self.allowpull:
968 return
968 return
969
969
970 if req.form.has_key('roots'):
970 if req.form.has_key('roots'):
971 nodes = map(bin, req.form['roots'][0].split(" "))
971 nodes = map(bin, req.form['roots'][0].split(" "))
972
972
973 z = zlib.compressobj()
973 z = zlib.compressobj()
974 f = self.repo.changegroup(nodes, 'serve')
974 f = self.repo.changegroup(nodes, 'serve')
975 while 1:
975 while 1:
976 chunk = f.read(4096)
976 chunk = f.read(4096)
977 if not chunk:
977 if not chunk:
978 break
978 break
979 req.write(z.compress(chunk))
979 req.write(z.compress(chunk))
980
980
981 req.write(z.flush())
981 req.write(z.flush())
982
982
983 def do_changegroupsubset(self, req):
983 def do_changegroupsubset(self, req):
984 req.httphdr("application/mercurial-0.1")
984 req.httphdr("application/mercurial-0.1")
985 bases = []
985 bases = []
986 heads = []
986 heads = []
987 if not self.allowpull:
987 if not self.allowpull:
988 return
988 return
989
989
990 if req.form.has_key('bases'):
990 if req.form.has_key('bases'):
991 bases = [bin(x) for x in req.form['bases'][0].split(' ')]
991 bases = [bin(x) for x in req.form['bases'][0].split(' ')]
992 if req.form.has_key('heads'):
992 if req.form.has_key('heads'):
993 heads = [bin(x) for x in req.form['heads'][0].split(' ')]
993 heads = [bin(x) for x in req.form['heads'][0].split(' ')]
994
994
995 z = zlib.compressobj()
995 z = zlib.compressobj()
996 f = self.repo.changegroupsubset(bases, heads, 'serve')
996 f = self.repo.changegroupsubset(bases, heads, 'serve')
997 while 1:
997 while 1:
998 chunk = f.read(4096)
998 chunk = f.read(4096)
999 if not chunk:
999 if not chunk:
1000 break
1000 break
1001 req.write(z.compress(chunk))
1001 req.write(z.compress(chunk))
1002
1002
1003 req.write(z.flush())
1003 req.write(z.flush())
1004
1004
1005 def do_archive(self, req):
1005 def do_archive(self, req):
1006 type_ = req.form['type'][0]
1006 type_ = req.form['type'][0]
1007 allowed = self.configlist("web", "allow_archive")
1007 allowed = self.configlist("web", "allow_archive")
1008 if (type_ in self.archives and (type_ in allowed or
1008 if (type_ in self.archives and (type_ in allowed or
1009 self.configbool("web", "allow" + type_, False))):
1009 self.configbool("web", "allow" + type_, False))):
1010 self.archive(req, req.form['node'][0], type_)
1010 self.archive(req, req.form['node'][0], type_)
1011 return
1011 return
1012
1012
1013 req.write(self.t("error"))
1013 req.write(self.t("error"))
1014
1014
1015 def do_static(self, req):
1015 def do_static(self, req):
1016 fname = req.form['file'][0]
1016 fname = req.form['file'][0]
1017 # a repo owner may set web.static in .hg/hgrc to get any file
1017 # a repo owner may set web.static in .hg/hgrc to get any file
1018 # readable by the user running the CGI script
1018 # readable by the user running the CGI script
1019 static = self.config("web", "static",
1019 static = self.config("web", "static",
1020 os.path.join(self.templatepath, "static"),
1020 os.path.join(self.templatepath, "static"),
1021 untrusted=False)
1021 untrusted=False)
1022 req.write(staticfile(static, fname, req)
1022 req.write(staticfile(static, fname, req)
1023 or self.t("error", error="%r not found" % fname))
1023 or self.t("error", error="%r not found" % fname))
1024
1024
1025 def do_capabilities(self, req):
1025 def do_capabilities(self, req):
1026 caps = ['lookup', 'changegroupsubset']
1026 caps = ['lookup', 'changegroupsubset']
1027 if self.configbool('server', 'uncompressed'):
1027 if self.configbool('server', 'uncompressed'):
1028 caps.append('stream=%d' % self.repo.changelog.version)
1028 caps.append('stream=%d' % self.repo.changelog.version)
1029 # XXX: make configurable and/or share code with do_unbundle:
1029 # XXX: make configurable and/or share code with do_unbundle:
1030 unbundleversions = ['HG10GZ', 'HG10BZ', 'HG10UN']
1030 unbundleversions = ['HG10GZ', 'HG10BZ', 'HG10UN']
1031 if unbundleversions:
1031 if unbundleversions:
1032 caps.append('unbundle=%s' % ','.join(unbundleversions))
1032 caps.append('unbundle=%s' % ','.join(unbundleversions))
1033 resp = ' '.join(caps)
1033 resp = ' '.join(caps)
1034 req.httphdr("application/mercurial-0.1", length=len(resp))
1034 req.httphdr("application/mercurial-0.1", length=len(resp))
1035 req.write(resp)
1035 req.write(resp)
1036
1036
1037 def check_perm(self, req, op, default):
1037 def check_perm(self, req, op, default):
1038 '''check permission for operation based on user auth.
1038 '''check permission for operation based on user auth.
1039 return true if op allowed, else false.
1039 return true if op allowed, else false.
1040 default is policy to use if no config given.'''
1040 default is policy to use if no config given.'''
1041
1041
1042 user = req.env.get('REMOTE_USER')
1042 user = req.env.get('REMOTE_USER')
1043
1043
1044 deny = self.configlist('web', 'deny_' + op)
1044 deny = self.configlist('web', 'deny_' + op)
1045 if deny and (not user or deny == ['*'] or user in deny):
1045 if deny and (not user or deny == ['*'] or user in deny):
1046 return False
1046 return False
1047
1047
1048 allow = self.configlist('web', 'allow_' + op)
1048 allow = self.configlist('web', 'allow_' + op)
1049 return (allow and (allow == ['*'] or user in allow)) or default
1049 return (allow and (allow == ['*'] or user in allow)) or default
1050
1050
1051 def do_unbundle(self, req):
1051 def do_unbundle(self, req):
1052 def bail(response, headers={}):
1052 def bail(response, headers={}):
1053 length = int(req.env['CONTENT_LENGTH'])
1053 length = int(req.env['CONTENT_LENGTH'])
1054 for s in util.filechunkiter(req, limit=length):
1054 for s in util.filechunkiter(req, limit=length):
1055 # drain incoming bundle, else client will not see
1055 # drain incoming bundle, else client will not see
1056 # response when run outside cgi script
1056 # response when run outside cgi script
1057 pass
1057 pass
1058 req.httphdr("application/mercurial-0.1", headers=headers)
1058 req.httphdr("application/mercurial-0.1", headers=headers)
1059 req.write('0\n')
1059 req.write('0\n')
1060 req.write(response)
1060 req.write(response)
1061
1061
1062 # require ssl by default, auth info cannot be sniffed and
1062 # require ssl by default, auth info cannot be sniffed and
1063 # replayed
1063 # replayed
1064 ssl_req = self.configbool('web', 'push_ssl', True)
1064 ssl_req = self.configbool('web', 'push_ssl', True)
1065 if ssl_req:
1065 if ssl_req:
1066 if not req.env.get('HTTPS'):
1066 if not req.env.get('HTTPS'):
1067 bail(_('ssl required\n'))
1067 bail(_('ssl required\n'))
1068 return
1068 return
1069 proto = 'https'
1069 proto = 'https'
1070 else:
1070 else:
1071 proto = 'http'
1071 proto = 'http'
1072
1072
1073 # do not allow push unless explicitly allowed
1073 # do not allow push unless explicitly allowed
1074 if not self.check_perm(req, 'push', False):
1074 if not self.check_perm(req, 'push', False):
1075 bail(_('push not authorized\n'),
1075 bail(_('push not authorized\n'),
1076 headers={'status': '401 Unauthorized'})
1076 headers={'status': '401 Unauthorized'})
1077 return
1077 return
1078
1078
1079 their_heads = req.form['heads'][0].split(' ')
1079 their_heads = req.form['heads'][0].split(' ')
1080
1080
1081 def check_heads():
1081 def check_heads():
1082 heads = map(hex, self.repo.heads())
1082 heads = map(hex, self.repo.heads())
1083 return their_heads == [hex('force')] or their_heads == heads
1083 return their_heads == [hex('force')] or their_heads == heads
1084
1084
1085 # fail early if possible
1085 # fail early if possible
1086 if not check_heads():
1086 if not check_heads():
1087 bail(_('unsynced changes\n'))
1087 bail(_('unsynced changes\n'))
1088 return
1088 return
1089
1089
1090 req.httphdr("application/mercurial-0.1")
1090 req.httphdr("application/mercurial-0.1")
1091
1091
1092 # do not lock repo until all changegroup data is
1092 # do not lock repo until all changegroup data is
1093 # streamed. save to temporary file.
1093 # streamed. save to temporary file.
1094
1094
1095 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
1095 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
1096 fp = os.fdopen(fd, 'wb+')
1096 fp = os.fdopen(fd, 'wb+')
1097 try:
1097 try:
1098 length = int(req.env['CONTENT_LENGTH'])
1098 length = int(req.env['CONTENT_LENGTH'])
1099 for s in util.filechunkiter(req, limit=length):
1099 for s in util.filechunkiter(req, limit=length):
1100 fp.write(s)
1100 fp.write(s)
1101
1101
1102 try:
1102 try:
1103 lock = self.repo.lock()
1103 lock = self.repo.lock()
1104 try:
1104 try:
1105 if not check_heads():
1105 if not check_heads():
1106 req.write('0\n')
1106 req.write('0\n')
1107 req.write(_('unsynced changes\n'))
1107 req.write(_('unsynced changes\n'))
1108 return
1108 return
1109
1109
1110 fp.seek(0)
1110 fp.seek(0)
1111 header = fp.read(6)
1111 header = fp.read(6)
1112 if not header.startswith("HG"):
1112 if not header.startswith("HG"):
1113 # old client with uncompressed bundle
1113 # old client with uncompressed bundle
1114 def generator(f):
1114 def generator(f):
1115 yield header
1115 yield header
1116 for chunk in f:
1116 for chunk in f:
1117 yield chunk
1117 yield chunk
1118 elif not header.startswith("HG10"):
1118 elif not header.startswith("HG10"):
1119 req.write("0\n")
1119 req.write("0\n")
1120 req.write(_("unknown bundle version\n"))
1120 req.write(_("unknown bundle version\n"))
1121 return
1121 return
1122 elif header == "HG10GZ":
1122 elif header == "HG10GZ":
1123 def generator(f):
1123 def generator(f):
1124 zd = zlib.decompressobj()
1124 zd = zlib.decompressobj()
1125 for chunk in f:
1125 for chunk in f:
1126 yield zd.decompress(chunk)
1126 yield zd.decompress(chunk)
1127 elif header == "HG10BZ":
1127 elif header == "HG10BZ":
1128 def generator(f):
1128 def generator(f):
1129 zd = bz2.BZ2Decompressor()
1129 zd = bz2.BZ2Decompressor()
1130 zd.decompress("BZ")
1130 zd.decompress("BZ")
1131 for chunk in f:
1131 for chunk in f:
1132 yield zd.decompress(chunk)
1132 yield zd.decompress(chunk)
1133 elif header == "HG10UN":
1133 elif header == "HG10UN":
1134 def generator(f):
1134 def generator(f):
1135 for chunk in f:
1135 for chunk in f:
1136 yield chunk
1136 yield chunk
1137 else:
1137 else:
1138 req.write("0\n")
1138 req.write("0\n")
1139 req.write(_("unknown bundle compression type\n"))
1139 req.write(_("unknown bundle compression type\n"))
1140 return
1140 return
1141 gen = generator(util.filechunkiter(fp, 4096))
1141 gen = generator(util.filechunkiter(fp, 4096))
1142
1142
1143 # send addchangegroup output to client
1143 # send addchangegroup output to client
1144
1144
1145 old_stdout = sys.stdout
1145 old_stdout = sys.stdout
1146 sys.stdout = cStringIO.StringIO()
1146 sys.stdout = cStringIO.StringIO()
1147
1147
1148 try:
1148 try:
1149 url = 'remote:%s:%s' % (proto,
1149 url = 'remote:%s:%s' % (proto,
1150 req.env.get('REMOTE_HOST', ''))
1150 req.env.get('REMOTE_HOST', ''))
1151 try:
1151 try:
1152 ret = self.repo.addchangegroup(
1152 ret = self.repo.addchangegroup(
1153 util.chunkbuffer(gen), 'serve', url)
1153 util.chunkbuffer(gen), 'serve', url)
1154 except util.Abort, inst:
1154 except util.Abort, inst:
1155 sys.stdout.write("abort: %s\n" % inst)
1155 sys.stdout.write("abort: %s\n" % inst)
1156 ret = 0
1156 ret = 0
1157 finally:
1157 finally:
1158 val = sys.stdout.getvalue()
1158 val = sys.stdout.getvalue()
1159 sys.stdout = old_stdout
1159 sys.stdout = old_stdout
1160 req.write('%d\n' % ret)
1160 req.write('%d\n' % ret)
1161 req.write(val)
1161 req.write(val)
1162 finally:
1162 finally:
1163 lock.release()
1163 lock.release()
1164 except (OSError, IOError), inst:
1164 except (OSError, IOError), inst:
1165 req.write('0\n')
1165 req.write('0\n')
1166 filename = getattr(inst, 'filename', '')
1166 filename = getattr(inst, 'filename', '')
1167 # Don't send our filesystem layout to the client
1167 # Don't send our filesystem layout to the client
1168 if filename.startswith(self.repo.root):
1168 if filename.startswith(self.repo.root):
1169 filename = filename[len(self.repo.root)+1:]
1169 filename = filename[len(self.repo.root)+1:]
1170 else:
1170 else:
1171 filename = ''
1171 filename = ''
1172 error = getattr(inst, 'strerror', 'Unknown error')
1172 error = getattr(inst, 'strerror', 'Unknown error')
1173 req.write('%s: %s\n' % (error, filename))
1173 req.write('%s: %s\n' % (error, filename))
1174 finally:
1174 finally:
1175 fp.close()
1175 fp.close()
1176 os.unlink(tempname)
1176 os.unlink(tempname)
1177
1177
1178 def do_stream_out(self, req):
1178 def do_stream_out(self, req):
1179 req.httphdr("application/mercurial-0.1")
1179 req.httphdr("application/mercurial-0.1")
1180 streamclone.stream_out(self.repo, req)
1180 streamclone.stream_out(self.repo, req)
@@ -1,290 +1,297 b''
1 # templater.py - template expansion for output
1 # templater.py - template expansion for output
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 i18n import _
8 from i18n import _
9 from node import *
9 from node import *
10 import cgi, re, sys, os, time, urllib, util, textwrap
10 import cgi, re, sys, os, time, urllib, util, textwrap
11
11
12 def parsestring(s, quoted=True):
12 def parsestring(s, quoted=True):
13 '''parse a string using simple c-like syntax.
13 '''parse a string using simple c-like syntax.
14 string must be in quotes if quoted is True.'''
14 string must be in quotes if quoted is True.'''
15 if quoted:
15 if quoted:
16 if len(s) < 2 or s[0] != s[-1]:
16 if len(s) < 2 or s[0] != s[-1]:
17 raise SyntaxError(_('unmatched quotes'))
17 raise SyntaxError(_('unmatched quotes'))
18 return s[1:-1].decode('string_escape')
18 return s[1:-1].decode('string_escape')
19
19
20 return s.decode('string_escape')
20 return s.decode('string_escape')
21
21
22 class templater(object):
22 class templater(object):
23 '''template expansion engine.
23 '''template expansion engine.
24
24
25 template expansion works like this. a map file contains key=value
25 template expansion works like this. a map file contains key=value
26 pairs. if value is quoted, it is treated as string. otherwise, it
26 pairs. if value is quoted, it is treated as string. otherwise, it
27 is treated as name of template file.
27 is treated as name of template file.
28
28
29 templater is asked to expand a key in map. it looks up key, and
29 templater is asked to expand a key in map. it looks up key, and
30 looks for strings like this: {foo}. it expands {foo} by looking up
30 looks for strings like this: {foo}. it expands {foo} by looking up
31 foo in map, and substituting it. expansion is recursive: it stops
31 foo in map, and substituting it. expansion is recursive: it stops
32 when there is no more {foo} to replace.
32 when there is no more {foo} to replace.
33
33
34 expansion also allows formatting and filtering.
34 expansion also allows formatting and filtering.
35
35
36 format uses key to expand each item in list. syntax is
36 format uses key to expand each item in list. syntax is
37 {key%format}.
37 {key%format}.
38
38
39 filter uses function to transform value. syntax is
39 filter uses function to transform value. syntax is
40 {key|filter1|filter2|...}.'''
40 {key|filter1|filter2|...}.'''
41
41
42 template_re = re.compile(r"(?:(?:#(?=[\w\|%]+#))|(?:{(?=[\w\|%]+})))"
42 template_re = re.compile(r"(?:(?:#(?=[\w\|%]+#))|(?:{(?=[\w\|%]+})))"
43 r"(\w+)(?:(?:%(\w+))|((?:\|\w+)*))[#}]")
43 r"(\w+)(?:(?:%(\w+))|((?:\|\w+)*))[#}]")
44
44
45 def __init__(self, mapfile, filters={}, defaults={}, cache={}):
45 def __init__(self, mapfile, filters={}, defaults={}, cache={}):
46 '''set up template engine.
46 '''set up template engine.
47 mapfile is name of file to read map definitions from.
47 mapfile is name of file to read map definitions from.
48 filters is dict of functions. each transforms a value into another.
48 filters is dict of functions. each transforms a value into another.
49 defaults is dict of default map definitions.'''
49 defaults is dict of default map definitions.'''
50 self.mapfile = mapfile or 'template'
50 self.mapfile = mapfile or 'template'
51 self.cache = cache.copy()
51 self.cache = cache.copy()
52 self.map = {}
52 self.map = {}
53 self.base = (mapfile and os.path.dirname(mapfile)) or ''
53 self.base = (mapfile and os.path.dirname(mapfile)) or ''
54 self.filters = filters
54 self.filters = filters
55 self.defaults = defaults
55 self.defaults = defaults
56
56
57 if not mapfile:
57 if not mapfile:
58 return
58 return
59 i = 0
59 i = 0
60 for l in file(mapfile):
60 for l in file(mapfile):
61 l = l.strip()
61 l = l.strip()
62 i += 1
62 i += 1
63 if not l or l[0] in '#;': continue
63 if not l or l[0] in '#;': continue
64 m = re.match(r'([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$', l)
64 m = re.match(r'([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*(.+)$', l)
65 if m:
65 if m:
66 key, val = m.groups()
66 key, val = m.groups()
67 if val[0] in "'\"":
67 if val[0] in "'\"":
68 try:
68 try:
69 self.cache[key] = parsestring(val)
69 self.cache[key] = parsestring(val)
70 except SyntaxError, inst:
70 except SyntaxError, inst:
71 raise SyntaxError('%s:%s: %s' %
71 raise SyntaxError('%s:%s: %s' %
72 (mapfile, i, inst.args[0]))
72 (mapfile, i, inst.args[0]))
73 else:
73 else:
74 self.map[key] = os.path.join(self.base, val)
74 self.map[key] = os.path.join(self.base, val)
75 else:
75 else:
76 raise SyntaxError(_("%s:%s: parse error") % (mapfile, i))
76 raise SyntaxError(_("%s:%s: parse error") % (mapfile, i))
77
77
78 def __contains__(self, key):
78 def __contains__(self, key):
79 return key in self.cache or key in self.map
79 return key in self.cache or key in self.map
80
80
81 def __call__(self, t, **map):
81 def __call__(self, t, **map):
82 '''perform expansion.
82 '''perform expansion.
83 t is name of map element to expand.
83 t is name of map element to expand.
84 map is added elements to use during expansion.'''
84 map is added elements to use during expansion.'''
85 if not self.cache.has_key(t):
85 if not self.cache.has_key(t):
86 try:
86 try:
87 self.cache[t] = file(self.map[t]).read()
87 self.cache[t] = file(self.map[t]).read()
88 except IOError, inst:
88 except IOError, inst:
89 raise IOError(inst.args[0], _('template file %s: %s') %
89 raise IOError(inst.args[0], _('template file %s: %s') %
90 (self.map[t], inst.args[1]))
90 (self.map[t], inst.args[1]))
91 tmpl = self.cache[t]
91 tmpl = self.cache[t]
92
92
93 while tmpl:
93 while tmpl:
94 m = self.template_re.search(tmpl)
94 m = self.template_re.search(tmpl)
95 if not m:
95 if not m:
96 yield tmpl
96 yield tmpl
97 break
97 break
98
98
99 start, end = m.span(0)
99 start, end = m.span(0)
100 key, format, fl = m.groups()
100 key, format, fl = m.groups()
101
101
102 if start:
102 if start:
103 yield tmpl[:start]
103 yield tmpl[:start]
104 tmpl = tmpl[end:]
104 tmpl = tmpl[end:]
105
105
106 if key in map:
106 if key in map:
107 v = map[key]
107 v = map[key]
108 else:
108 else:
109 v = self.defaults.get(key, "")
109 v = self.defaults.get(key, "")
110 if callable(v):
110 if callable(v):
111 v = v(**map)
111 v = v(**map)
112 if format:
112 if format:
113 if not hasattr(v, '__iter__'):
113 if not hasattr(v, '__iter__'):
114 raise SyntaxError(_("Error expanding '%s%s'")
114 raise SyntaxError(_("Error expanding '%s%s'")
115 % (key, format))
115 % (key, format))
116 lm = map.copy()
116 lm = map.copy()
117 for i in v:
117 for i in v:
118 lm.update(i)
118 lm.update(i)
119 yield self(format, **lm)
119 yield self(format, **lm)
120 else:
120 else:
121 if fl:
121 if fl:
122 for f in fl.split("|")[1:]:
122 for f in fl.split("|")[1:]:
123 v = self.filters[f](v)
123 v = self.filters[f](v)
124 yield v
124 yield v
125
125
126 agescales = [("second", 1),
126 agescales = [("second", 1),
127 ("minute", 60),
127 ("minute", 60),
128 ("hour", 3600),
128 ("hour", 3600),
129 ("day", 3600 * 24),
129 ("day", 3600 * 24),
130 ("week", 3600 * 24 * 7),
130 ("week", 3600 * 24 * 7),
131 ("month", 3600 * 24 * 30),
131 ("month", 3600 * 24 * 30),
132 ("year", 3600 * 24 * 365)]
132 ("year", 3600 * 24 * 365)]
133
133
134 agescales.reverse()
134 agescales.reverse()
135
135
136 def age(date):
136 def age(date):
137 '''turn a (timestamp, tzoff) tuple into an age string.'''
137 '''turn a (timestamp, tzoff) tuple into an age string.'''
138
138
139 def plural(t, c):
139 def plural(t, c):
140 if c == 1:
140 if c == 1:
141 return t
141 return t
142 return t + "s"
142 return t + "s"
143 def fmt(t, c):
143 def fmt(t, c):
144 return "%d %s" % (c, plural(t, c))
144 return "%d %s" % (c, plural(t, c))
145
145
146 now = time.time()
146 now = time.time()
147 then = date[0]
147 then = date[0]
148 delta = max(1, int(now - then))
148 delta = max(1, int(now - then))
149
149
150 for t, s in agescales:
150 for t, s in agescales:
151 n = delta / s
151 n = delta / s
152 if n >= 2 or s == 1:
152 if n >= 2 or s == 1:
153 return fmt(t, n)
153 return fmt(t, n)
154
154
155 def stringify(thing):
155 def stringify(thing):
156 '''turn nested template iterator into string.'''
156 '''turn nested template iterator into string.'''
157 if hasattr(thing, '__iter__'):
157 if hasattr(thing, '__iter__'):
158 return "".join([stringify(t) for t in thing if t is not None])
158 return "".join([stringify(t) for t in thing if t is not None])
159 return str(thing)
159 return str(thing)
160
160
161 para_re = None
161 para_re = None
162 space_re = None
162 space_re = None
163
163
164 def fill(text, width):
164 def fill(text, width):
165 '''fill many paragraphs.'''
165 '''fill many paragraphs.'''
166 global para_re, space_re
166 global para_re, space_re
167 if para_re is None:
167 if para_re is None:
168 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
168 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
169 space_re = re.compile(r' +')
169 space_re = re.compile(r' +')
170
170
171 def findparas():
171 def findparas():
172 start = 0
172 start = 0
173 while True:
173 while True:
174 m = para_re.search(text, start)
174 m = para_re.search(text, start)
175 if not m:
175 if not m:
176 w = len(text)
176 w = len(text)
177 while w > start and text[w-1].isspace(): w -= 1
177 while w > start and text[w-1].isspace(): w -= 1
178 yield text[start:w], text[w:]
178 yield text[start:w], text[w:]
179 break
179 break
180 yield text[start:m.start(0)], m.group(1)
180 yield text[start:m.start(0)], m.group(1)
181 start = m.end(1)
181 start = m.end(1)
182
182
183 return "".join([space_re.sub(' ', textwrap.fill(para, width)) + rest
183 return "".join([space_re.sub(' ', textwrap.fill(para, width)) + rest
184 for para, rest in findparas()])
184 for para, rest in findparas()])
185
185
186 def firstline(text):
186 def firstline(text):
187 '''return the first line of text'''
187 '''return the first line of text'''
188 try:
188 try:
189 return text.splitlines(1)[0].rstrip('\r\n')
189 return text.splitlines(1)[0].rstrip('\r\n')
190 except IndexError:
190 except IndexError:
191 return ''
191 return ''
192
192
193 def isodate(date):
193 def isodate(date):
194 '''turn a (timestamp, tzoff) tuple into an iso 8631 date and time.'''
194 '''turn a (timestamp, tzoff) tuple into an iso 8631 date and time.'''
195 return util.datestr(date, format='%Y-%m-%d %H:%M')
195 return util.datestr(date, format='%Y-%m-%d %H:%M')
196
196
197 def hgdate(date):
197 def hgdate(date):
198 '''turn a (timestamp, tzoff) tuple into an hg cset timestamp.'''
198 '''turn a (timestamp, tzoff) tuple into an hg cset timestamp.'''
199 return "%d %d" % date
199 return "%d %d" % date
200
200
201 def nl2br(text):
201 def nl2br(text):
202 '''replace raw newlines with xhtml line breaks.'''
202 '''replace raw newlines with xhtml line breaks.'''
203 return text.replace('\n', '<br/>\n')
203 return text.replace('\n', '<br/>\n')
204
204
205 def obfuscate(text):
205 def obfuscate(text):
206 text = unicode(text, util._encoding, 'replace')
206 text = unicode(text, util._encoding, 'replace')
207 return ''.join(['&#%d;' % ord(c) for c in text])
207 return ''.join(['&#%d;' % ord(c) for c in text])
208
208
209 def domain(author):
209 def domain(author):
210 '''get domain of author, or empty string if none.'''
210 '''get domain of author, or empty string if none.'''
211 f = author.find('@')
211 f = author.find('@')
212 if f == -1: return ''
212 if f == -1: return ''
213 author = author[f+1:]
213 author = author[f+1:]
214 f = author.find('>')
214 f = author.find('>')
215 if f >= 0: author = author[:f]
215 if f >= 0: author = author[:f]
216 return author
216 return author
217
217
218 def email(author):
218 def email(author):
219 '''get email of author.'''
219 '''get email of author.'''
220 r = author.find('>')
220 r = author.find('>')
221 if r == -1: r = None
221 if r == -1: r = None
222 return author[author.find('<')+1:r]
222 return author[author.find('<')+1:r]
223
223
224 def person(author):
224 def person(author):
225 '''get name of author, or else username.'''
225 '''get name of author, or else username.'''
226 f = author.find('<')
226 f = author.find('<')
227 if f == -1: return util.shortuser(author)
227 if f == -1: return util.shortuser(author)
228 return author[:f].rstrip()
228 return author[:f].rstrip()
229
229
230 def shortdate(date):
230 def shortdate(date):
231 '''turn (timestamp, tzoff) tuple into iso 8631 date.'''
231 '''turn (timestamp, tzoff) tuple into iso 8631 date.'''
232 return util.datestr(date, format='%Y-%m-%d', timezone=False)
232 return util.datestr(date, format='%Y-%m-%d', timezone=False)
233
233
234 def indent(text, prefix):
234 def indent(text, prefix):
235 '''indent each non-empty line of text after first with prefix.'''
235 '''indent each non-empty line of text after first with prefix.'''
236 lines = text.splitlines()
236 lines = text.splitlines()
237 num_lines = len(lines)
237 num_lines = len(lines)
238 def indenter():
238 def indenter():
239 for i in xrange(num_lines):
239 for i in xrange(num_lines):
240 l = lines[i]
240 l = lines[i]
241 if i and l.strip():
241 if i and l.strip():
242 yield prefix
242 yield prefix
243 yield l
243 yield l
244 if i < num_lines - 1 or text.endswith('\n'):
244 if i < num_lines - 1 or text.endswith('\n'):
245 yield '\n'
245 yield '\n'
246 return "".join(indenter())
246 return "".join(indenter())
247
247
248 def permissions(flags):
249 if "l" in flags:
250 return "lrwxrwxrwx"
251 if "x" in flags:
252 return "-rwxr-xr-x"
253 return "-rw-r--r--"
254
248 common_filters = {
255 common_filters = {
249 "addbreaks": nl2br,
256 "addbreaks": nl2br,
250 "basename": os.path.basename,
257 "basename": os.path.basename,
251 "age": age,
258 "age": age,
252 "date": lambda x: util.datestr(x),
259 "date": lambda x: util.datestr(x),
253 "domain": domain,
260 "domain": domain,
254 "email": email,
261 "email": email,
255 "escape": lambda x: cgi.escape(x, True),
262 "escape": lambda x: cgi.escape(x, True),
256 "fill68": lambda x: fill(x, width=68),
263 "fill68": lambda x: fill(x, width=68),
257 "fill76": lambda x: fill(x, width=76),
264 "fill76": lambda x: fill(x, width=76),
258 "firstline": firstline,
265 "firstline": firstline,
259 "tabindent": lambda x: indent(x, '\t'),
266 "tabindent": lambda x: indent(x, '\t'),
260 "hgdate": hgdate,
267 "hgdate": hgdate,
261 "isodate": isodate,
268 "isodate": isodate,
262 "obfuscate": obfuscate,
269 "obfuscate": obfuscate,
263 "permissions": lambda x: x and "-rwxr-xr-x" or "-rw-r--r--",
270 "permissions": permissions,
264 "person": person,
271 "person": person,
265 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
272 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
266 "short": lambda x: x[:12],
273 "short": lambda x: x[:12],
267 "shortdate": shortdate,
274 "shortdate": shortdate,
268 "stringify": stringify,
275 "stringify": stringify,
269 "strip": lambda x: x.strip(),
276 "strip": lambda x: x.strip(),
270 "urlescape": lambda x: urllib.quote(x),
277 "urlescape": lambda x: urllib.quote(x),
271 "user": lambda x: util.shortuser(x),
278 "user": lambda x: util.shortuser(x),
272 "stringescape": lambda x: x.encode('string_escape'),
279 "stringescape": lambda x: x.encode('string_escape'),
273 }
280 }
274
281
275 def templatepath(name=None):
282 def templatepath(name=None):
276 '''return location of template file or directory (if no name).
283 '''return location of template file or directory (if no name).
277 returns None if not found.'''
284 returns None if not found.'''
278
285
279 # executable version (py2exe) doesn't support __file__
286 # executable version (py2exe) doesn't support __file__
280 if hasattr(sys, 'frozen'):
287 if hasattr(sys, 'frozen'):
281 module = sys.executable
288 module = sys.executable
282 else:
289 else:
283 module = __file__
290 module = __file__
284 for f in 'templates', '../templates':
291 for f in 'templates', '../templates':
285 fl = f.split('/')
292 fl = f.split('/')
286 if name: fl.append(name)
293 if name: fl.append(name)
287 p = os.path.join(os.path.dirname(module), *fl)
294 p = os.path.join(os.path.dirname(module), *fl)
288 if (name and os.path.exists(p)) or os.path.isdir(p):
295 if (name and os.path.exists(p)) or os.path.isdir(p):
289 return os.path.normpath(p)
296 return os.path.normpath(p)
290
297
@@ -1,53 +1,70 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 hg init
3 hg init
4 echo foo > foo
4 echo foo > foo
5 echo "# should fail - foo is not managed"
6 hg mv foo bar
7 hg st -A
5 hg add foo
8 hg add foo
9 echo "# dry-run; print a warning that this is not a real copy; foo is added"
10 hg mv --dry-run foo bar
11 hg st -A
12 echo "# should print a warning that this is not a real copy; bar is added"
13 hg mv foo bar
14 hg st -A
15 echo "# should print a warning that this is not a real copy; foo is added"
16 hg cp bar foo
17 hg rm -f bar
18 rm bar
19 hg st -A
6 hg commit -m1 -d"0 0"
20 hg commit -m1 -d"0 0"
7
21
22 echo "# dry-run; should show that foo is clean"
23 hg copy --dry-run foo bar
24 hg st -A
8 echo "# should show copy"
25 echo "# should show copy"
9 hg copy foo bar
26 hg copy foo bar
10 hg debugstate|grep '^copy'
27 hg st -C
11
28
12 echo "# shouldn't show copy"
29 echo "# shouldn't show copy"
13 hg commit -m2 -d"0 0"
30 hg commit -m2 -d"0 0"
14 hg debugstate|grep '^copy'
31 hg st -C
15
32
16 echo "# should match"
33 echo "# should match"
17 hg debugindex .hg/store/data/foo.i
34 hg debugindex .hg/store/data/foo.i
18 hg debugrename bar
35 hg debugrename bar
19
36
20 echo bleah > foo
37 echo bleah > foo
21 echo quux > bar
38 echo quux > bar
22 hg commit -m3 -d"0 0"
39 hg commit -m3 -d"0 0"
23
40
24 echo "# should not be renamed"
41 echo "# should not be renamed"
25 hg debugrename bar
42 hg debugrename bar
26
43
27 hg copy -f foo bar
44 hg copy -f foo bar
28 echo "# should show copy"
45 echo "# should show copy"
29 hg debugstate|grep '^copy'
46 hg st -C
30 hg commit -m3 -d"0 0"
47 hg commit -m3 -d"0 0"
31
48
32 echo "# should show no parents for tip"
49 echo "# should show no parents for tip"
33 hg debugindex .hg/store/data/bar.i
50 hg debugindex .hg/store/data/bar.i
34 echo "# should match"
51 echo "# should match"
35 hg debugindex .hg/store/data/foo.i
52 hg debugindex .hg/store/data/foo.i
36 hg debugrename bar
53 hg debugrename bar
37
54
38 echo "# should show no copies"
55 echo "# should show no copies"
39 hg debugstate|grep '^copy'
56 hg st -C
40
57
41 echo "# copy --after on an added file"
58 echo "# copy --after on an added file"
42 cp bar baz
59 cp bar baz
43 hg add baz
60 hg add baz
44 hg cp -A bar baz
61 hg cp -A bar baz
45 hg st -C
62 hg st -C
46
63
47 echo "# foo was clean:"
64 echo "# foo was clean:"
48 hg st -AC foo
65 hg st -AC foo
49 echo "# but it's considered modified after a copy --after --force"
66 echo "# but it's considered modified after a copy --after --force"
50 hg copy -Af bar foo
67 hg copy -Af bar foo
51 hg st -AC foo
68 hg st -AC foo
52
69
53 exit 0
70 exit 0
@@ -1,30 +1,47 b''
1 # should fail - foo is not managed
2 foo: not copying - file is not managed
3 abort: no files to copy
4 ? foo
5 # dry-run; print a warning that this is not a real copy; foo is added
6 foo was marked for addition. bar will not be committed as a copy.
7 A foo
8 # should print a warning that this is not a real copy; bar is added
9 foo was marked for addition. bar will not be committed as a copy.
10 A bar
11 # should print a warning that this is not a real copy; foo is added
12 bar was marked for addition. foo will not be committed as a copy.
13 A foo
14 # dry-run; should show that foo is clean
15 C foo
1 # should show copy
16 # should show copy
2 copy: foo -> bar
17 A bar
18 foo
3 # shouldn't show copy
19 # shouldn't show copy
4 # should match
20 # should match
5 rev offset length base linkrev nodeid p1 p2
21 rev offset length base linkrev nodeid p1 p2
6 0 0 5 0 0 2ed2a3912a0b 000000000000 000000000000
22 0 0 5 0 0 2ed2a3912a0b 000000000000 000000000000
7 bar renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd
23 bar renamed from foo:2ed2a3912a0b24502043eae84ee4b279c18b90dd
8 # should not be renamed
24 # should not be renamed
9 bar not renamed
25 bar not renamed
10 # should show copy
26 # should show copy
11 copy: foo -> bar
27 M bar
28 foo
12 # should show no parents for tip
29 # should show no parents for tip
13 rev offset length base linkrev nodeid p1 p2
30 rev offset length base linkrev nodeid p1 p2
14 0 0 69 0 1 6ca237634e1f 000000000000 000000000000
31 0 0 69 0 1 6ca237634e1f 000000000000 000000000000
15 1 69 6 1 2 7a1ff8e75f5b 6ca237634e1f 000000000000
32 1 69 6 1 2 7a1ff8e75f5b 6ca237634e1f 000000000000
16 2 75 82 1 3 243dfe60f3d9 000000000000 000000000000
33 2 75 82 1 3 243dfe60f3d9 000000000000 000000000000
17 # should match
34 # should match
18 rev offset length base linkrev nodeid p1 p2
35 rev offset length base linkrev nodeid p1 p2
19 0 0 5 0 0 2ed2a3912a0b 000000000000 000000000000
36 0 0 5 0 0 2ed2a3912a0b 000000000000 000000000000
20 1 5 7 1 2 dd12c926cf16 2ed2a3912a0b 000000000000
37 1 5 7 1 2 dd12c926cf16 2ed2a3912a0b 000000000000
21 bar renamed from foo:dd12c926cf165e3eb4cf87b084955cb617221c17
38 bar renamed from foo:dd12c926cf165e3eb4cf87b084955cb617221c17
22 # should show no copies
39 # should show no copies
23 # copy --after on an added file
40 # copy --after on an added file
24 A baz
41 A baz
25 bar
42 bar
26 # foo was clean:
43 # foo was clean:
27 C foo
44 C foo
28 # but it's considered modified after a copy --after --force
45 # but it's considered modified after a copy --after --force
29 M foo
46 M foo
30 bar
47 bar
@@ -1,211 +1,210 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 hg init
3 hg init
4 mkdir d1 d1/d11 d2
4 mkdir d1 d1/d11 d2
5 echo d1/a > d1/a
5 echo d1/a > d1/a
6 echo d1/ba > d1/ba
6 echo d1/ba > d1/ba
7 echo d1/a1 > d1/d11/a1
7 echo d1/a1 > d1/d11/a1
8 echo d1/b > d1/b
8 echo d1/b > d1/b
9 echo d2/b > d2/b
9 echo d2/b > d2/b
10 hg add d1/a d1/b d1/ba d1/d11/a1 d2/b
10 hg add d1/a d1/b d1/ba d1/d11/a1 d2/b
11 hg commit -m "1" -d "1000000 0"
11 hg commit -m "1" -d "1000000 0"
12
12
13 echo "# rename a single file"
13 echo "# rename a single file"
14 hg rename d1/d11/a1 d2/c
14 hg rename d1/d11/a1 d2/c
15 hg status
15 hg status -C
16 hg update -C
16 hg update -C
17
17
18 echo "# rename --after a single file"
18 echo "# rename --after a single file"
19 mv d1/d11/a1 d2/c
19 mv d1/d11/a1 d2/c
20 hg rename --after d1/d11/a1 d2/c
20 hg rename --after d1/d11/a1 d2/c
21 hg status
21 hg status -C
22 hg update -C
22 hg update -C
23
23
24 echo "# move a single file to an existing directory"
24 echo "# move a single file to an existing directory"
25 hg rename d1/d11/a1 d2
25 hg rename d1/d11/a1 d2
26 hg status
26 hg status -C
27 hg update -C
27 hg update -C
28
28
29 echo "# move --after a single file to an existing directory"
29 echo "# move --after a single file to an existing directory"
30 mv d1/d11/a1 d2
30 mv d1/d11/a1 d2
31 hg rename --after d1/d11/a1 d2
31 hg rename --after d1/d11/a1 d2
32 hg status
32 hg status -C
33 hg update -C
33 hg update -C
34
34
35 echo "# rename a file using a relative path"
35 echo "# rename a file using a relative path"
36 (cd d1/d11; hg rename ../../d2/b e)
36 (cd d1/d11; hg rename ../../d2/b e)
37 hg status
37 hg status -C
38 hg update -C
38 hg update -C
39
39
40 echo "# rename --after a file using a relative path"
40 echo "# rename --after a file using a relative path"
41 (cd d1/d11; mv ../../d2/b e; hg rename --after ../../d2/b e)
41 (cd d1/d11; mv ../../d2/b e; hg rename --after ../../d2/b e)
42 hg status
42 hg status -C
43 hg update -C
43 hg update -C
44
44
45 echo "# rename directory d1 as d3"
45 echo "# rename directory d1 as d3"
46 hg rename d1/ d3
46 hg rename d1/ d3
47 hg status
47 hg status -C
48 hg update -C
48 hg update -C
49
49
50 echo "# rename --after directory d1 as d3"
50 echo "# rename --after directory d1 as d3"
51 mv d1 d3
51 mv d1 d3
52 hg rename --after d1 d3
52 hg rename --after d1 d3
53 hg status
53 hg status -C
54 hg update -C
54 hg update -C
55
55
56 echo "# move a directory using a relative path"
56 echo "# move a directory using a relative path"
57 (cd d2; mkdir d3; hg rename ../d1/d11 d3)
57 (cd d2; mkdir d3; hg rename ../d1/d11 d3)
58 hg status
58 hg status -C
59 hg update -C
59 hg update -C
60
60
61 echo "# move --after a directory using a relative path"
61 echo "# move --after a directory using a relative path"
62 (cd d2; mkdir d3; mv ../d1/d11 d3; hg rename --after ../d1/d11 d3)
62 (cd d2; mkdir d3; mv ../d1/d11 d3; hg rename --after ../d1/d11 d3)
63 hg status
63 hg status -C
64 hg update -C
64 hg update -C
65
65
66 echo "# move directory d1/d11 to an existing directory d2 (removes empty d1)"
66 echo "# move directory d1/d11 to an existing directory d2 (removes empty d1)"
67 hg rename d1/d11/ d2
67 hg rename d1/d11/ d2
68 hg status
68 hg status -C
69 hg update -C
69 hg update -C
70
70
71 echo "# move directories d1 and d2 to a new directory d3"
71 echo "# move directories d1 and d2 to a new directory d3"
72 mkdir d3
72 mkdir d3
73 hg rename d1 d2 d3
73 hg rename d1 d2 d3
74 hg status
74 hg status -C
75 hg update -C
75 hg update -C
76
76
77 echo "# move --after directories d1 and d2 to a new directory d3"
77 echo "# move --after directories d1 and d2 to a new directory d3"
78 mkdir d3
78 mkdir d3
79 mv d1 d2 d3
79 mv d1 d2 d3
80 hg rename --after d1 d2 d3
80 hg rename --after d1 d2 d3
81 hg status
81 hg status -C
82 hg update -C
82 hg update -C
83
83
84 echo "# move everything under directory d1 to existing directory d2, do not"
84 echo "# move everything under directory d1 to existing directory d2, do not"
85 echo "# overwrite existing files (d2/b)"
85 echo "# overwrite existing files (d2/b)"
86 hg rename d1/* d2
86 hg rename d1/* d2
87 hg status
87 hg status -C
88 diff d1/b d2/b
88 diff d1/b d2/b
89 hg update -C
89 hg update -C
90
90
91 echo "# attempt to move potentially more than one file into a non-existent"
91 echo "# attempt to move potentially more than one file into a non-existent"
92 echo "# directory"
92 echo "# directory"
93 hg rename 'glob:d1/**' dx
93 hg rename 'glob:d1/**' dx
94
94
95 echo "# move every file under d1 to d2/d21 (glob)"
95 echo "# move every file under d1 to d2/d21 (glob)"
96 mkdir d2/d21
96 mkdir d2/d21
97 hg rename 'glob:d1/**' d2/d21
97 hg rename 'glob:d1/**' d2/d21
98 hg status
98 hg status -C
99 hg update -C
99 hg update -C
100
100
101 echo "# move --after some files under d1 to d2/d21 (glob)"
101 echo "# move --after some files under d1 to d2/d21 (glob)"
102 mkdir d2/d21
102 mkdir d2/d21
103 mv d1/a d1/d11/a1 d2/d21
103 mv d1/a d1/d11/a1 d2/d21
104 hg rename --after 'glob:d1/**' d2/d21
104 hg rename --after 'glob:d1/**' d2/d21
105 hg status
105 hg status -C
106 hg update -C
106 hg update -C
107
107
108 echo "# move every file under d1 starting with an 'a' to d2/d21 (regexp)"
108 echo "# move every file under d1 starting with an 'a' to d2/d21 (regexp)"
109 mkdir d2/d21
109 mkdir d2/d21
110 hg rename 're:d1/([^a][^/]*/)*a.*' d2/d21
110 hg rename 're:d1/([^a][^/]*/)*a.*' d2/d21
111 hg status
111 hg status -C
112 hg update -C
112 hg update -C
113
113
114 echo "# attempt to overwrite an existing file"
114 echo "# attempt to overwrite an existing file"
115 echo "ca" > d1/ca
115 echo "ca" > d1/ca
116 hg rename d1/ba d1/ca
116 hg rename d1/ba d1/ca
117 hg status
117 hg status -C
118 hg update -C
118 hg update -C
119
119
120 echo "# forced overwrite of an existing file"
120 echo "# forced overwrite of an existing file"
121 echo "ca" > d1/ca
121 echo "ca" > d1/ca
122 hg rename --force d1/ba d1/ca
122 hg rename --force d1/ba d1/ca
123 hg status
123 hg status -C
124 hg update -C
124 hg update -C
125
125
126 echo "# replace a symlink with a file"
126 echo "# replace a symlink with a file"
127 ln -s ba d1/ca
127 ln -s ba d1/ca
128 hg rename --force d1/ba d1/ca
128 hg rename --force d1/ba d1/ca
129 hg status
129 hg status -C
130 hg update -C
130 hg update -C
131
131
132 echo "# do not copy more than one source file to the same destination file"
132 echo "# do not copy more than one source file to the same destination file"
133 mkdir d3
133 mkdir d3
134 hg rename d1/* d2/* d3
134 hg rename d1/* d2/* d3
135 hg status
135 hg status -C
136 hg update -C
136 hg update -C
137
137
138 echo "# move a whole subtree with \"hg rename .\""
138 echo "# move a whole subtree with \"hg rename .\""
139 mkdir d3
139 mkdir d3
140 (cd d1; hg rename . ../d3)
140 (cd d1; hg rename . ../d3)
141 hg status
141 hg status -C
142 hg update -C
142 hg update -C
143
143
144 echo "# move a whole subtree with \"hg rename --after .\""
144 echo "# move a whole subtree with \"hg rename --after .\""
145 mkdir d3
145 mkdir d3
146 mv d1/* d3
146 mv d1/* d3
147 (cd d1; hg rename --after . ../d3)
147 (cd d1; hg rename --after . ../d3)
148 hg status
148 hg status -C
149 hg update -C
149 hg update -C
150
150
151 echo "# move the parent tree with \"hg rename ..\""
151 echo "# move the parent tree with \"hg rename ..\""
152 (cd d1/d11; hg rename .. ../../d3)
152 (cd d1/d11; hg rename .. ../../d3)
153 hg status
153 hg status -C
154 hg update -C
154 hg update -C
155
155
156 echo "# skip removed files"
156 echo "# skip removed files"
157 hg remove d1/b
157 hg remove d1/b
158 hg rename d1 d3
158 hg rename d1 d3
159 hg status
159 hg status -C
160 hg update -C
160 hg update -C
161
161
162 echo "# transitive rename"
162 echo "# transitive rename"
163 hg rename d1/b d1/bb
163 hg rename d1/b d1/bb
164 hg rename d1/bb d1/bc
164 hg rename d1/bb d1/bc
165 hg status
165 hg status -C
166 hg update -C
166 hg update -C
167
167
168 echo "# transitive rename --after"
168 echo "# transitive rename --after"
169 hg rename d1/b d1/bb
169 hg rename d1/b d1/bb
170 mv d1/bb d1/bc
170 mv d1/bb d1/bc
171 hg rename --after d1/bb d1/bc
171 hg rename --after d1/bb d1/bc
172 hg status
172 hg status -C
173 hg update -C
173 hg update -C
174
174
175 echo "# idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)"
175 echo "# idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)"
176 hg rename d1/b d1/bb
176 hg rename d1/b d1/bb
177 echo "some stuff added to d1/bb" >> d1/bb
177 echo "some stuff added to d1/bb" >> d1/bb
178 hg rename d1/bb d1/b
178 hg rename d1/bb d1/b
179 hg status
179 hg status -C
180 hg debugstate | grep copy
181 hg update -C
180 hg update -C
182
181
183 echo "# check illegal path components"
182 echo "# check illegal path components"
184
183
185 hg rename d1/d11/a1 .hg/foo
184 hg rename d1/d11/a1 .hg/foo
186 hg status
185 hg status -C
187 hg rename d1/d11/a1 ../foo
186 hg rename d1/d11/a1 ../foo
188 hg status
187 hg status -C
189
188
190 mv d1/d11/a1 .hg/foo
189 mv d1/d11/a1 .hg/foo
191 hg rename --after d1/d11/a1 .hg/foo
190 hg rename --after d1/d11/a1 .hg/foo
192 hg status
191 hg status -C
193 hg update -C
192 hg update -C
194 rm .hg/foo
193 rm .hg/foo
195
194
196 hg rename d1/d11/a1 .hg
195 hg rename d1/d11/a1 .hg
197 hg status
196 hg status -C
198 hg rename d1/d11/a1 ..
197 hg rename d1/d11/a1 ..
199 hg status
198 hg status -C
200
199
201 mv d1/d11/a1 .hg
200 mv d1/d11/a1 .hg
202 hg rename --after d1/d11/a1 .hg
201 hg rename --after d1/d11/a1 .hg
203 hg status
202 hg status -C
204 hg update -C
203 hg update -C
205 rm .hg/a1
204 rm .hg/a1
206
205
207 (cd d1/d11; hg rename ../../d2/b ../../.hg/foo)
206 (cd d1/d11; hg rename ../../d2/b ../../.hg/foo)
208 hg status
207 hg status -C
209 (cd d1/d11; hg rename ../../d2/b ../../../foo)
208 (cd d1/d11; hg rename ../../d2/b ../../../foo)
210 hg status
209 hg status -C
211
210
@@ -1,297 +1,358 b''
1 # rename a single file
1 # rename a single file
2 A d2/c
2 A d2/c
3 d1/d11/a1
3 R d1/d11/a1
4 R d1/d11/a1
4 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
5 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
5 # rename --after a single file
6 # rename --after a single file
6 A d2/c
7 A d2/c
8 d1/d11/a1
7 R d1/d11/a1
9 R d1/d11/a1
8 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
10 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
9 # move a single file to an existing directory
11 # move a single file to an existing directory
10 A d2/a1
12 A d2/a1
13 d1/d11/a1
11 R d1/d11/a1
14 R d1/d11/a1
12 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
15 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
13 # move --after a single file to an existing directory
16 # move --after a single file to an existing directory
14 A d2/a1
17 A d2/a1
18 d1/d11/a1
15 R d1/d11/a1
19 R d1/d11/a1
16 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
20 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
17 # rename a file using a relative path
21 # rename a file using a relative path
18 A d1/d11/e
22 A d1/d11/e
23 d2/b
19 R d2/b
24 R d2/b
20 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
25 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
21 # rename --after a file using a relative path
26 # rename --after a file using a relative path
22 A d1/d11/e
27 A d1/d11/e
28 d2/b
23 R d2/b
29 R d2/b
24 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
30 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
25 # rename directory d1 as d3
31 # rename directory d1 as d3
26 copying d1/a to d3/a
32 copying d1/a to d3/a
27 copying d1/b to d3/b
33 copying d1/b to d3/b
28 copying d1/ba to d3/ba
34 copying d1/ba to d3/ba
29 copying d1/d11/a1 to d3/d11/a1
35 copying d1/d11/a1 to d3/d11/a1
30 removing d1/a
36 removing d1/a
31 removing d1/b
37 removing d1/b
32 removing d1/ba
38 removing d1/ba
33 removing d1/d11/a1
39 removing d1/d11/a1
34 A d3/a
40 A d3/a
41 d1/a
35 A d3/b
42 A d3/b
43 d1/b
36 A d3/ba
44 A d3/ba
45 d1/ba
37 A d3/d11/a1
46 A d3/d11/a1
47 d1/d11/a1
38 R d1/a
48 R d1/a
39 R d1/b
49 R d1/b
40 R d1/ba
50 R d1/ba
41 R d1/d11/a1
51 R d1/d11/a1
42 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
52 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
43 # rename --after directory d1 as d3
53 # rename --after directory d1 as d3
44 copying d1/a to d3/a
54 copying d1/a to d3/a
45 copying d1/b to d3/b
55 copying d1/b to d3/b
46 copying d1/ba to d3/ba
56 copying d1/ba to d3/ba
47 copying d1/d11/a1 to d3/d11/a1
57 copying d1/d11/a1 to d3/d11/a1
48 removing d1/a
58 removing d1/a
49 removing d1/b
59 removing d1/b
50 removing d1/ba
60 removing d1/ba
51 removing d1/d11/a1
61 removing d1/d11/a1
52 A d3/a
62 A d3/a
63 d1/a
53 A d3/b
64 A d3/b
65 d1/b
54 A d3/ba
66 A d3/ba
67 d1/ba
55 A d3/d11/a1
68 A d3/d11/a1
69 d1/d11/a1
56 R d1/a
70 R d1/a
57 R d1/b
71 R d1/b
58 R d1/ba
72 R d1/ba
59 R d1/d11/a1
73 R d1/d11/a1
60 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
74 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
61 # move a directory using a relative path
75 # move a directory using a relative path
62 copying ../d1/d11/a1 to d3/d11/a1
76 copying ../d1/d11/a1 to d3/d11/a1
63 removing ../d1/d11/a1
77 removing ../d1/d11/a1
64 A d2/d3/d11/a1
78 A d2/d3/d11/a1
79 d1/d11/a1
65 R d1/d11/a1
80 R d1/d11/a1
66 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
81 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
67 # move --after a directory using a relative path
82 # move --after a directory using a relative path
68 copying ../d1/d11/a1 to d3/d11/a1
83 copying ../d1/d11/a1 to d3/d11/a1
69 removing ../d1/d11/a1
84 removing ../d1/d11/a1
70 A d2/d3/d11/a1
85 A d2/d3/d11/a1
86 d1/d11/a1
71 R d1/d11/a1
87 R d1/d11/a1
72 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
88 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
73 # move directory d1/d11 to an existing directory d2 (removes empty d1)
89 # move directory d1/d11 to an existing directory d2 (removes empty d1)
74 copying d1/d11/a1 to d2/d11/a1
90 copying d1/d11/a1 to d2/d11/a1
75 removing d1/d11/a1
91 removing d1/d11/a1
76 A d2/d11/a1
92 A d2/d11/a1
93 d1/d11/a1
77 R d1/d11/a1
94 R d1/d11/a1
78 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
95 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
79 # move directories d1 and d2 to a new directory d3
96 # move directories d1 and d2 to a new directory d3
80 copying d1/a to d3/d1/a
97 copying d1/a to d3/d1/a
81 copying d1/b to d3/d1/b
98 copying d1/b to d3/d1/b
82 copying d1/ba to d3/d1/ba
99 copying d1/ba to d3/d1/ba
83 copying d1/d11/a1 to d3/d1/d11/a1
100 copying d1/d11/a1 to d3/d1/d11/a1
84 copying d2/b to d3/d2/b
101 copying d2/b to d3/d2/b
85 removing d1/a
102 removing d1/a
86 removing d1/b
103 removing d1/b
87 removing d1/ba
104 removing d1/ba
88 removing d1/d11/a1
105 removing d1/d11/a1
89 removing d2/b
106 removing d2/b
90 A d3/d1/a
107 A d3/d1/a
108 d1/a
91 A d3/d1/b
109 A d3/d1/b
110 d1/b
92 A d3/d1/ba
111 A d3/d1/ba
112 d1/ba
93 A d3/d1/d11/a1
113 A d3/d1/d11/a1
114 d1/d11/a1
94 A d3/d2/b
115 A d3/d2/b
116 d2/b
95 R d1/a
117 R d1/a
96 R d1/b
118 R d1/b
97 R d1/ba
119 R d1/ba
98 R d1/d11/a1
120 R d1/d11/a1
99 R d2/b
121 R d2/b
100 5 files updated, 0 files merged, 5 files removed, 0 files unresolved
122 5 files updated, 0 files merged, 5 files removed, 0 files unresolved
101 # move --after directories d1 and d2 to a new directory d3
123 # move --after directories d1 and d2 to a new directory d3
102 copying d1/a to d3/d1/a
124 copying d1/a to d3/d1/a
103 copying d1/b to d3/d1/b
125 copying d1/b to d3/d1/b
104 copying d1/ba to d3/d1/ba
126 copying d1/ba to d3/d1/ba
105 copying d1/d11/a1 to d3/d1/d11/a1
127 copying d1/d11/a1 to d3/d1/d11/a1
106 copying d2/b to d3/d2/b
128 copying d2/b to d3/d2/b
107 removing d1/a
129 removing d1/a
108 removing d1/b
130 removing d1/b
109 removing d1/ba
131 removing d1/ba
110 removing d1/d11/a1
132 removing d1/d11/a1
111 removing d2/b
133 removing d2/b
112 A d3/d1/a
134 A d3/d1/a
135 d1/a
113 A d3/d1/b
136 A d3/d1/b
137 d1/b
114 A d3/d1/ba
138 A d3/d1/ba
139 d1/ba
115 A d3/d1/d11/a1
140 A d3/d1/d11/a1
141 d1/d11/a1
116 A d3/d2/b
142 A d3/d2/b
143 d2/b
117 R d1/a
144 R d1/a
118 R d1/b
145 R d1/b
119 R d1/ba
146 R d1/ba
120 R d1/d11/a1
147 R d1/d11/a1
121 R d2/b
148 R d2/b
122 5 files updated, 0 files merged, 5 files removed, 0 files unresolved
149 5 files updated, 0 files merged, 5 files removed, 0 files unresolved
123 # move everything under directory d1 to existing directory d2, do not
150 # move everything under directory d1 to existing directory d2, do not
124 # overwrite existing files (d2/b)
151 # overwrite existing files (d2/b)
125 d2/b: not overwriting - file exists
152 d2/b: not overwriting - file exists
126 copying d1/d11/a1 to d2/d11/a1
153 copying d1/d11/a1 to d2/d11/a1
127 removing d1/d11/a1
154 removing d1/d11/a1
128 A d2/a
155 A d2/a
156 d1/a
129 A d2/ba
157 A d2/ba
158 d1/ba
130 A d2/d11/a1
159 A d2/d11/a1
160 d1/d11/a1
131 R d1/a
161 R d1/a
132 R d1/ba
162 R d1/ba
133 R d1/d11/a1
163 R d1/d11/a1
134 1c1
164 1c1
135 < d1/b
165 < d1/b
136 ---
166 ---
137 > d2/b
167 > d2/b
138 3 files updated, 0 files merged, 3 files removed, 0 files unresolved
168 3 files updated, 0 files merged, 3 files removed, 0 files unresolved
139 # attempt to move potentially more than one file into a non-existent
169 # attempt to move potentially more than one file into a non-existent
140 # directory
170 # directory
141 abort: with multiple sources, destination must be an existing directory
171 abort: with multiple sources, destination must be an existing directory
142 # move every file under d1 to d2/d21 (glob)
172 # move every file under d1 to d2/d21 (glob)
143 copying d1/a to d2/d21/a
173 copying d1/a to d2/d21/a
144 copying d1/b to d2/d21/b
174 copying d1/b to d2/d21/b
145 copying d1/ba to d2/d21/ba
175 copying d1/ba to d2/d21/ba
146 copying d1/d11/a1 to d2/d21/a1
176 copying d1/d11/a1 to d2/d21/a1
147 removing d1/a
177 removing d1/a
148 removing d1/b
178 removing d1/b
149 removing d1/ba
179 removing d1/ba
150 removing d1/d11/a1
180 removing d1/d11/a1
151 A d2/d21/a
181 A d2/d21/a
182 d1/a
152 A d2/d21/a1
183 A d2/d21/a1
184 d1/d11/a1
153 A d2/d21/b
185 A d2/d21/b
186 d1/b
154 A d2/d21/ba
187 A d2/d21/ba
188 d1/ba
155 R d1/a
189 R d1/a
156 R d1/b
190 R d1/b
157 R d1/ba
191 R d1/ba
158 R d1/d11/a1
192 R d1/d11/a1
159 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
193 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
160 # move --after some files under d1 to d2/d21 (glob)
194 # move --after some files under d1 to d2/d21 (glob)
161 copying d1/a to d2/d21/a
195 copying d1/a to d2/d21/a
162 copying d1/d11/a1 to d2/d21/a1
196 copying d1/d11/a1 to d2/d21/a1
163 removing d1/a
197 removing d1/a
164 removing d1/d11/a1
198 removing d1/d11/a1
165 A d2/d21/a
199 A d2/d21/a
200 d1/a
166 A d2/d21/a1
201 A d2/d21/a1
202 d1/d11/a1
167 R d1/a
203 R d1/a
168 R d1/d11/a1
204 R d1/d11/a1
169 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
205 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
170 # move every file under d1 starting with an 'a' to d2/d21 (regexp)
206 # move every file under d1 starting with an 'a' to d2/d21 (regexp)
171 copying d1/a to d2/d21/a
207 copying d1/a to d2/d21/a
172 copying d1/d11/a1 to d2/d21/a1
208 copying d1/d11/a1 to d2/d21/a1
173 removing d1/a
209 removing d1/a
174 removing d1/d11/a1
210 removing d1/d11/a1
175 A d2/d21/a
211 A d2/d21/a
212 d1/a
176 A d2/d21/a1
213 A d2/d21/a1
214 d1/d11/a1
177 R d1/a
215 R d1/a
178 R d1/d11/a1
216 R d1/d11/a1
179 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
217 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
180 # attempt to overwrite an existing file
218 # attempt to overwrite an existing file
181 d1/ca: not overwriting - file exists
219 d1/ca: not overwriting - file exists
182 ? d1/ca
220 ? d1/ca
183 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
221 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
184 # forced overwrite of an existing file
222 # forced overwrite of an existing file
185 A d1/ca
223 A d1/ca
224 d1/ba
186 R d1/ba
225 R d1/ba
187 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
226 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
188 # replace a symlink with a file
227 # replace a symlink with a file
189 A d1/ca
228 A d1/ca
229 d1/ba
190 R d1/ba
230 R d1/ba
191 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
231 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
192 # do not copy more than one source file to the same destination file
232 # do not copy more than one source file to the same destination file
193 copying d1/d11/a1 to d3/d11/a1
233 copying d1/d11/a1 to d3/d11/a1
194 d3/b: not overwriting - d2/b collides with d1/b
234 d3/b: not overwriting - d2/b collides with d1/b
195 removing d1/d11/a1
235 removing d1/d11/a1
196 A d3/a
236 A d3/a
237 d1/a
197 A d3/b
238 A d3/b
239 d1/b
198 A d3/ba
240 A d3/ba
241 d1/ba
199 A d3/d11/a1
242 A d3/d11/a1
243 d1/d11/a1
200 R d1/a
244 R d1/a
201 R d1/b
245 R d1/b
202 R d1/ba
246 R d1/ba
203 R d1/d11/a1
247 R d1/d11/a1
204 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
248 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
205 # move a whole subtree with "hg rename ."
249 # move a whole subtree with "hg rename ."
206 copying a to ../d3/d1/a
250 copying a to ../d3/d1/a
207 copying b to ../d3/d1/b
251 copying b to ../d3/d1/b
208 copying ba to ../d3/d1/ba
252 copying ba to ../d3/d1/ba
209 copying d11/a1 to ../d3/d1/d11/a1
253 copying d11/a1 to ../d3/d1/d11/a1
210 removing a
254 removing a
211 removing b
255 removing b
212 removing ba
256 removing ba
213 removing d11/a1
257 removing d11/a1
214 A d3/d1/a
258 A d3/d1/a
259 d1/a
215 A d3/d1/b
260 A d3/d1/b
261 d1/b
216 A d3/d1/ba
262 A d3/d1/ba
263 d1/ba
217 A d3/d1/d11/a1
264 A d3/d1/d11/a1
265 d1/d11/a1
218 R d1/a
266 R d1/a
219 R d1/b
267 R d1/b
220 R d1/ba
268 R d1/ba
221 R d1/d11/a1
269 R d1/d11/a1
222 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
270 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
223 # move a whole subtree with "hg rename --after ."
271 # move a whole subtree with "hg rename --after ."
224 copying a to ../d3/a
272 copying a to ../d3/a
225 copying b to ../d3/b
273 copying b to ../d3/b
226 copying ba to ../d3/ba
274 copying ba to ../d3/ba
227 copying d11/a1 to ../d3/d11/a1
275 copying d11/a1 to ../d3/d11/a1
228 removing a
276 removing a
229 removing b
277 removing b
230 removing ba
278 removing ba
231 removing d11/a1
279 removing d11/a1
232 A d3/a
280 A d3/a
281 d1/a
233 A d3/b
282 A d3/b
283 d1/b
234 A d3/ba
284 A d3/ba
285 d1/ba
235 A d3/d11/a1
286 A d3/d11/a1
287 d1/d11/a1
236 R d1/a
288 R d1/a
237 R d1/b
289 R d1/b
238 R d1/ba
290 R d1/ba
239 R d1/d11/a1
291 R d1/d11/a1
240 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
292 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
241 # move the parent tree with "hg rename .."
293 # move the parent tree with "hg rename .."
242 copying ../a to ../../d3/a
294 copying ../a to ../../d3/a
243 copying ../b to ../../d3/b
295 copying ../b to ../../d3/b
244 copying ../ba to ../../d3/ba
296 copying ../ba to ../../d3/ba
245 copying a1 to ../../d3/d11/a1
297 copying a1 to ../../d3/d11/a1
246 removing ../a
298 removing ../a
247 removing ../b
299 removing ../b
248 removing ../ba
300 removing ../ba
249 removing a1
301 removing a1
250 A d3/a
302 A d3/a
303 d1/a
251 A d3/b
304 A d3/b
305 d1/b
252 A d3/ba
306 A d3/ba
307 d1/ba
253 A d3/d11/a1
308 A d3/d11/a1
309 d1/d11/a1
254 R d1/a
310 R d1/a
255 R d1/b
311 R d1/b
256 R d1/ba
312 R d1/ba
257 R d1/d11/a1
313 R d1/d11/a1
258 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
314 4 files updated, 0 files merged, 4 files removed, 0 files unresolved
259 # skip removed files
315 # skip removed files
260 copying d1/a to d3/a
316 copying d1/a to d3/a
261 copying d1/ba to d3/ba
317 copying d1/ba to d3/ba
262 copying d1/d11/a1 to d3/d11/a1
318 copying d1/d11/a1 to d3/d11/a1
263 removing d1/a
319 removing d1/a
264 removing d1/ba
320 removing d1/ba
265 removing d1/d11/a1
321 removing d1/d11/a1
266 A d3/a
322 A d3/a
323 d1/a
267 A d3/ba
324 A d3/ba
325 d1/ba
268 A d3/d11/a1
326 A d3/d11/a1
327 d1/d11/a1
269 R d1/a
328 R d1/a
270 R d1/b
329 R d1/b
271 R d1/ba
330 R d1/ba
272 R d1/d11/a1
331 R d1/d11/a1
273 4 files updated, 0 files merged, 3 files removed, 0 files unresolved
332 4 files updated, 0 files merged, 3 files removed, 0 files unresolved
274 # transitive rename
333 # transitive rename
275 A d1/bc
334 A d1/bc
335 d1/b
276 R d1/b
336 R d1/b
277 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
337 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
278 # transitive rename --after
338 # transitive rename --after
279 A d1/bc
339 A d1/bc
340 d1/b
280 R d1/b
341 R d1/b
281 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
342 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
282 # idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)
343 # idempotent renames (d1/b -> d1/bb followed by d1/bb -> d1/b)
283 M d1/b
344 M d1/b
284 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
345 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
285 # check illegal path components
346 # check illegal path components
286 abort: path contains illegal component: .hg/foo
347 abort: path contains illegal component: .hg/foo
287 abort: ../foo not under root
348 abort: ../foo not under root
288 abort: path contains illegal component: .hg/foo
349 abort: path contains illegal component: .hg/foo
289 ! d1/d11/a1
350 ! d1/d11/a1
290 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
351 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
291 abort: path contains illegal component: .hg/a1
352 abort: path contains illegal component: .hg/a1
292 abort: ../a1 not under root
353 abort: ../a1 not under root
293 abort: path contains illegal component: .hg/a1
354 abort: path contains illegal component: .hg/a1
294 ! d1/d11/a1
355 ! d1/d11/a1
295 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
356 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
296 abort: path contains illegal component: .hg/foo
357 abort: path contains illegal component: .hg/foo
297 abort: ../../../foo not under root
358 abort: ../../../foo not under root
General Comments 0
You need to be logged in to leave comments. Login now