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