##// END OF EJS Templates
help: consolidate topic hooks in help.py...
Matt Mackall -
r14318:1f46be46 default
parent child Browse files
Show More
@@ -1,5026 +1,5022 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 of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, bin, nullid, nullrev, short
8 from node import hex, bin, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _, gettext
10 from i18n import _, gettext
11 import os, re, sys, difflib, time, tempfile
11 import os, re, sys, difflib, time, tempfile
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
12 import hg, scmutil, util, revlog, extensions, copies, error, bookmarks
13 import patch, help, url, encoding, templatekw, discovery
13 import patch, help, url, encoding, templatekw, discovery
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
14 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
15 import merge as mergemod
15 import merge as mergemod
16 import minirst, revset, templatefilters
16 import minirst, revset
17 import dagparser, context, simplemerge
17 import dagparser, context, simplemerge
18 import random, setdiscovery, treediscovery, dagutil
18 import random, setdiscovery, treediscovery, dagutil
19
19
20 table = {}
20 table = {}
21
21
22 command = cmdutil.command(table)
22 command = cmdutil.command(table)
23
23
24 # common command options
24 # common command options
25
25
26 globalopts = [
26 globalopts = [
27 ('R', 'repository', '',
27 ('R', 'repository', '',
28 _('repository root directory or name of overlay bundle file'),
28 _('repository root directory or name of overlay bundle file'),
29 _('REPO')),
29 _('REPO')),
30 ('', 'cwd', '',
30 ('', 'cwd', '',
31 _('change working directory'), _('DIR')),
31 _('change working directory'), _('DIR')),
32 ('y', 'noninteractive', None,
32 ('y', 'noninteractive', None,
33 _('do not prompt, assume \'yes\' for any required answers')),
33 _('do not prompt, assume \'yes\' for any required answers')),
34 ('q', 'quiet', None, _('suppress output')),
34 ('q', 'quiet', None, _('suppress output')),
35 ('v', 'verbose', None, _('enable additional output')),
35 ('v', 'verbose', None, _('enable additional output')),
36 ('', 'config', [],
36 ('', 'config', [],
37 _('set/override config option (use \'section.name=value\')'),
37 _('set/override config option (use \'section.name=value\')'),
38 _('CONFIG')),
38 _('CONFIG')),
39 ('', 'debug', None, _('enable debugging output')),
39 ('', 'debug', None, _('enable debugging output')),
40 ('', 'debugger', None, _('start debugger')),
40 ('', 'debugger', None, _('start debugger')),
41 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
41 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
42 _('ENCODE')),
42 _('ENCODE')),
43 ('', 'encodingmode', encoding.encodingmode,
43 ('', 'encodingmode', encoding.encodingmode,
44 _('set the charset encoding mode'), _('MODE')),
44 _('set the charset encoding mode'), _('MODE')),
45 ('', 'traceback', None, _('always print a traceback on exception')),
45 ('', 'traceback', None, _('always print a traceback on exception')),
46 ('', 'time', None, _('time how long the command takes')),
46 ('', 'time', None, _('time how long the command takes')),
47 ('', 'profile', None, _('print command execution profile')),
47 ('', 'profile', None, _('print command execution profile')),
48 ('', 'version', None, _('output version information and exit')),
48 ('', 'version', None, _('output version information and exit')),
49 ('h', 'help', None, _('display help and exit')),
49 ('h', 'help', None, _('display help and exit')),
50 ]
50 ]
51
51
52 dryrunopts = [('n', 'dry-run', None,
52 dryrunopts = [('n', 'dry-run', None,
53 _('do not perform actions, just print output'))]
53 _('do not perform actions, just print output'))]
54
54
55 remoteopts = [
55 remoteopts = [
56 ('e', 'ssh', '',
56 ('e', 'ssh', '',
57 _('specify ssh command to use'), _('CMD')),
57 _('specify ssh command to use'), _('CMD')),
58 ('', 'remotecmd', '',
58 ('', 'remotecmd', '',
59 _('specify hg command to run on the remote side'), _('CMD')),
59 _('specify hg command to run on the remote side'), _('CMD')),
60 ('', 'insecure', None,
60 ('', 'insecure', None,
61 _('do not verify server certificate (ignoring web.cacerts config)')),
61 _('do not verify server certificate (ignoring web.cacerts config)')),
62 ]
62 ]
63
63
64 walkopts = [
64 walkopts = [
65 ('I', 'include', [],
65 ('I', 'include', [],
66 _('include names matching the given patterns'), _('PATTERN')),
66 _('include names matching the given patterns'), _('PATTERN')),
67 ('X', 'exclude', [],
67 ('X', 'exclude', [],
68 _('exclude names matching the given patterns'), _('PATTERN')),
68 _('exclude names matching the given patterns'), _('PATTERN')),
69 ]
69 ]
70
70
71 commitopts = [
71 commitopts = [
72 ('m', 'message', '',
72 ('m', 'message', '',
73 _('use text as commit message'), _('TEXT')),
73 _('use text as commit message'), _('TEXT')),
74 ('l', 'logfile', '',
74 ('l', 'logfile', '',
75 _('read commit message from file'), _('FILE')),
75 _('read commit message from file'), _('FILE')),
76 ]
76 ]
77
77
78 commitopts2 = [
78 commitopts2 = [
79 ('d', 'date', '',
79 ('d', 'date', '',
80 _('record the specified date as commit date'), _('DATE')),
80 _('record the specified date as commit date'), _('DATE')),
81 ('u', 'user', '',
81 ('u', 'user', '',
82 _('record the specified user as committer'), _('USER')),
82 _('record the specified user as committer'), _('USER')),
83 ]
83 ]
84
84
85 templateopts = [
85 templateopts = [
86 ('', 'style', '',
86 ('', 'style', '',
87 _('display using template map file'), _('STYLE')),
87 _('display using template map file'), _('STYLE')),
88 ('', 'template', '',
88 ('', 'template', '',
89 _('display with template'), _('TEMPLATE')),
89 _('display with template'), _('TEMPLATE')),
90 ]
90 ]
91
91
92 logopts = [
92 logopts = [
93 ('p', 'patch', None, _('show patch')),
93 ('p', 'patch', None, _('show patch')),
94 ('g', 'git', None, _('use git extended diff format')),
94 ('g', 'git', None, _('use git extended diff format')),
95 ('l', 'limit', '',
95 ('l', 'limit', '',
96 _('limit number of changes displayed'), _('NUM')),
96 _('limit number of changes displayed'), _('NUM')),
97 ('M', 'no-merges', None, _('do not show merges')),
97 ('M', 'no-merges', None, _('do not show merges')),
98 ('', 'stat', None, _('output diffstat-style summary of changes')),
98 ('', 'stat', None, _('output diffstat-style summary of changes')),
99 ] + templateopts
99 ] + templateopts
100
100
101 diffopts = [
101 diffopts = [
102 ('a', 'text', None, _('treat all files as text')),
102 ('a', 'text', None, _('treat all files as text')),
103 ('g', 'git', None, _('use git extended diff format')),
103 ('g', 'git', None, _('use git extended diff format')),
104 ('', 'nodates', None, _('omit dates from diff headers'))
104 ('', 'nodates', None, _('omit dates from diff headers'))
105 ]
105 ]
106
106
107 diffopts2 = [
107 diffopts2 = [
108 ('p', 'show-function', None, _('show which function each change is in')),
108 ('p', 'show-function', None, _('show which function each change is in')),
109 ('', 'reverse', None, _('produce a diff that undoes the changes')),
109 ('', 'reverse', None, _('produce a diff that undoes the changes')),
110 ('w', 'ignore-all-space', None,
110 ('w', 'ignore-all-space', None,
111 _('ignore white space when comparing lines')),
111 _('ignore white space when comparing lines')),
112 ('b', 'ignore-space-change', None,
112 ('b', 'ignore-space-change', None,
113 _('ignore changes in the amount of white space')),
113 _('ignore changes in the amount of white space')),
114 ('B', 'ignore-blank-lines', None,
114 ('B', 'ignore-blank-lines', None,
115 _('ignore changes whose lines are all blank')),
115 _('ignore changes whose lines are all blank')),
116 ('U', 'unified', '',
116 ('U', 'unified', '',
117 _('number of lines of context to show'), _('NUM')),
117 _('number of lines of context to show'), _('NUM')),
118 ('', 'stat', None, _('output diffstat-style summary of changes')),
118 ('', 'stat', None, _('output diffstat-style summary of changes')),
119 ]
119 ]
120
120
121 similarityopts = [
121 similarityopts = [
122 ('s', 'similarity', '',
122 ('s', 'similarity', '',
123 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
123 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
124 ]
124 ]
125
125
126 subrepoopts = [
126 subrepoopts = [
127 ('S', 'subrepos', None,
127 ('S', 'subrepos', None,
128 _('recurse into subrepositories'))
128 _('recurse into subrepositories'))
129 ]
129 ]
130
130
131 # Commands start here, listed alphabetically
131 # Commands start here, listed alphabetically
132
132
133 @command('^add',
133 @command('^add',
134 walkopts + subrepoopts + dryrunopts,
134 walkopts + subrepoopts + dryrunopts,
135 _('[OPTION]... [FILE]...'))
135 _('[OPTION]... [FILE]...'))
136 def add(ui, repo, *pats, **opts):
136 def add(ui, repo, *pats, **opts):
137 """add the specified files on the next commit
137 """add the specified files on the next commit
138
138
139 Schedule files to be version controlled and added to the
139 Schedule files to be version controlled and added to the
140 repository.
140 repository.
141
141
142 The files will be added to the repository at the next commit. To
142 The files will be added to the repository at the next commit. To
143 undo an add before that, see :hg:`forget`.
143 undo an add before that, see :hg:`forget`.
144
144
145 If no names are given, add all files to the repository.
145 If no names are given, add all files to the repository.
146
146
147 .. container:: verbose
147 .. container:: verbose
148
148
149 An example showing how new (unknown) files are added
149 An example showing how new (unknown) files are added
150 automatically by :hg:`add`::
150 automatically by :hg:`add`::
151
151
152 $ ls
152 $ ls
153 foo.c
153 foo.c
154 $ hg status
154 $ hg status
155 ? foo.c
155 ? foo.c
156 $ hg add
156 $ hg add
157 adding foo.c
157 adding foo.c
158 $ hg status
158 $ hg status
159 A foo.c
159 A foo.c
160
160
161 Returns 0 if all files are successfully added.
161 Returns 0 if all files are successfully added.
162 """
162 """
163
163
164 m = cmdutil.match(repo, pats, opts)
164 m = cmdutil.match(repo, pats, opts)
165 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
165 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
166 opts.get('subrepos'), prefix="")
166 opts.get('subrepos'), prefix="")
167 return rejected and 1 or 0
167 return rejected and 1 or 0
168
168
169 @command('addremove',
169 @command('addremove',
170 similarityopts + walkopts + dryrunopts,
170 similarityopts + walkopts + dryrunopts,
171 _('[OPTION]... [FILE]...'))
171 _('[OPTION]... [FILE]...'))
172 def addremove(ui, repo, *pats, **opts):
172 def addremove(ui, repo, *pats, **opts):
173 """add all new files, delete all missing files
173 """add all new files, delete all missing files
174
174
175 Add all new files and remove all missing files from the
175 Add all new files and remove all missing files from the
176 repository.
176 repository.
177
177
178 New files are ignored if they match any of the patterns in
178 New files are ignored if they match any of the patterns in
179 ``.hgignore``. As with add, these changes take effect at the next
179 ``.hgignore``. As with add, these changes take effect at the next
180 commit.
180 commit.
181
181
182 Use the -s/--similarity option to detect renamed files. With a
182 Use the -s/--similarity option to detect renamed files. With a
183 parameter greater than 0, this compares every removed file with
183 parameter greater than 0, this compares every removed file with
184 every added file and records those similar enough as renames. This
184 every added file and records those similar enough as renames. This
185 option takes a percentage between 0 (disabled) and 100 (files must
185 option takes a percentage between 0 (disabled) and 100 (files must
186 be identical) as its parameter. Detecting renamed files this way
186 be identical) as its parameter. Detecting renamed files this way
187 can be expensive. After using this option, :hg:`status -C` can be
187 can be expensive. After using this option, :hg:`status -C` can be
188 used to check which files were identified as moved or renamed.
188 used to check which files were identified as moved or renamed.
189
189
190 Returns 0 if all files are successfully added.
190 Returns 0 if all files are successfully added.
191 """
191 """
192 try:
192 try:
193 sim = float(opts.get('similarity') or 100)
193 sim = float(opts.get('similarity') or 100)
194 except ValueError:
194 except ValueError:
195 raise util.Abort(_('similarity must be a number'))
195 raise util.Abort(_('similarity must be a number'))
196 if sim < 0 or sim > 100:
196 if sim < 0 or sim > 100:
197 raise util.Abort(_('similarity must be between 0 and 100'))
197 raise util.Abort(_('similarity must be between 0 and 100'))
198 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
198 return cmdutil.addremove(repo, pats, opts, similarity=sim / 100.0)
199
199
200 @command('^annotate|blame',
200 @command('^annotate|blame',
201 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
201 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
202 ('', 'follow', None,
202 ('', 'follow', None,
203 _('follow copies/renames and list the filename (DEPRECATED)')),
203 _('follow copies/renames and list the filename (DEPRECATED)')),
204 ('', 'no-follow', None, _("don't follow copies and renames")),
204 ('', 'no-follow', None, _("don't follow copies and renames")),
205 ('a', 'text', None, _('treat all files as text')),
205 ('a', 'text', None, _('treat all files as text')),
206 ('u', 'user', None, _('list the author (long with -v)')),
206 ('u', 'user', None, _('list the author (long with -v)')),
207 ('f', 'file', None, _('list the filename')),
207 ('f', 'file', None, _('list the filename')),
208 ('d', 'date', None, _('list the date (short with -q)')),
208 ('d', 'date', None, _('list the date (short with -q)')),
209 ('n', 'number', None, _('list the revision number (default)')),
209 ('n', 'number', None, _('list the revision number (default)')),
210 ('c', 'changeset', None, _('list the changeset')),
210 ('c', 'changeset', None, _('list the changeset')),
211 ('l', 'line-number', None, _('show line number at the first appearance'))
211 ('l', 'line-number', None, _('show line number at the first appearance'))
212 ] + walkopts,
212 ] + walkopts,
213 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
213 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
214 def annotate(ui, repo, *pats, **opts):
214 def annotate(ui, repo, *pats, **opts):
215 """show changeset information by line for each file
215 """show changeset information by line for each file
216
216
217 List changes in files, showing the revision id responsible for
217 List changes in files, showing the revision id responsible for
218 each line
218 each line
219
219
220 This command is useful for discovering when a change was made and
220 This command is useful for discovering when a change was made and
221 by whom.
221 by whom.
222
222
223 Without the -a/--text option, annotate will avoid processing files
223 Without the -a/--text option, annotate will avoid processing files
224 it detects as binary. With -a, annotate will annotate the file
224 it detects as binary. With -a, annotate will annotate the file
225 anyway, although the results will probably be neither useful
225 anyway, although the results will probably be neither useful
226 nor desirable.
226 nor desirable.
227
227
228 Returns 0 on success.
228 Returns 0 on success.
229 """
229 """
230 if opts.get('follow'):
230 if opts.get('follow'):
231 # --follow is deprecated and now just an alias for -f/--file
231 # --follow is deprecated and now just an alias for -f/--file
232 # to mimic the behavior of Mercurial before version 1.5
232 # to mimic the behavior of Mercurial before version 1.5
233 opts['file'] = True
233 opts['file'] = True
234
234
235 datefunc = ui.quiet and util.shortdate or util.datestr
235 datefunc = ui.quiet and util.shortdate or util.datestr
236 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
236 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
237
237
238 if not pats:
238 if not pats:
239 raise util.Abort(_('at least one filename or pattern is required'))
239 raise util.Abort(_('at least one filename or pattern is required'))
240
240
241 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
241 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
242 ('number', lambda x: str(x[0].rev())),
242 ('number', lambda x: str(x[0].rev())),
243 ('changeset', lambda x: short(x[0].node())),
243 ('changeset', lambda x: short(x[0].node())),
244 ('date', getdate),
244 ('date', getdate),
245 ('file', lambda x: x[0].path()),
245 ('file', lambda x: x[0].path()),
246 ]
246 ]
247
247
248 if (not opts.get('user') and not opts.get('changeset')
248 if (not opts.get('user') and not opts.get('changeset')
249 and not opts.get('date') and not opts.get('file')):
249 and not opts.get('date') and not opts.get('file')):
250 opts['number'] = True
250 opts['number'] = True
251
251
252 linenumber = opts.get('line_number') is not None
252 linenumber = opts.get('line_number') is not None
253 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
253 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
254 raise util.Abort(_('at least one of -n/-c is required for -l'))
254 raise util.Abort(_('at least one of -n/-c is required for -l'))
255
255
256 funcmap = [func for op, func in opmap if opts.get(op)]
256 funcmap = [func for op, func in opmap if opts.get(op)]
257 if linenumber:
257 if linenumber:
258 lastfunc = funcmap[-1]
258 lastfunc = funcmap[-1]
259 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
259 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
260
260
261 def bad(x, y):
261 def bad(x, y):
262 raise util.Abort("%s: %s" % (x, y))
262 raise util.Abort("%s: %s" % (x, y))
263
263
264 ctx = cmdutil.revsingle(repo, opts.get('rev'))
264 ctx = cmdutil.revsingle(repo, opts.get('rev'))
265 m = cmdutil.match(repo, pats, opts)
265 m = cmdutil.match(repo, pats, opts)
266 m.bad = bad
266 m.bad = bad
267 follow = not opts.get('no_follow')
267 follow = not opts.get('no_follow')
268 for abs in ctx.walk(m):
268 for abs in ctx.walk(m):
269 fctx = ctx[abs]
269 fctx = ctx[abs]
270 if not opts.get('text') and util.binary(fctx.data()):
270 if not opts.get('text') and util.binary(fctx.data()):
271 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
271 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
272 continue
272 continue
273
273
274 lines = fctx.annotate(follow=follow, linenumber=linenumber)
274 lines = fctx.annotate(follow=follow, linenumber=linenumber)
275 pieces = []
275 pieces = []
276
276
277 for f in funcmap:
277 for f in funcmap:
278 l = [f(n) for n, dummy in lines]
278 l = [f(n) for n, dummy in lines]
279 if l:
279 if l:
280 sized = [(x, encoding.colwidth(x)) for x in l]
280 sized = [(x, encoding.colwidth(x)) for x in l]
281 ml = max([w for x, w in sized])
281 ml = max([w for x, w in sized])
282 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
282 pieces.append(["%s%s" % (' ' * (ml - w), x) for x, w in sized])
283
283
284 if pieces:
284 if pieces:
285 for p, l in zip(zip(*pieces), lines):
285 for p, l in zip(zip(*pieces), lines):
286 ui.write("%s: %s" % (" ".join(p), l[1]))
286 ui.write("%s: %s" % (" ".join(p), l[1]))
287
287
288 @command('archive',
288 @command('archive',
289 [('', 'no-decode', None, _('do not pass files through decoders')),
289 [('', 'no-decode', None, _('do not pass files through decoders')),
290 ('p', 'prefix', '', _('directory prefix for files in archive'),
290 ('p', 'prefix', '', _('directory prefix for files in archive'),
291 _('PREFIX')),
291 _('PREFIX')),
292 ('r', 'rev', '', _('revision to distribute'), _('REV')),
292 ('r', 'rev', '', _('revision to distribute'), _('REV')),
293 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
293 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
294 ] + subrepoopts + walkopts,
294 ] + subrepoopts + walkopts,
295 _('[OPTION]... DEST'))
295 _('[OPTION]... DEST'))
296 def archive(ui, repo, dest, **opts):
296 def archive(ui, repo, dest, **opts):
297 '''create an unversioned archive of a repository revision
297 '''create an unversioned archive of a repository revision
298
298
299 By default, the revision used is the parent of the working
299 By default, the revision used is the parent of the working
300 directory; use -r/--rev to specify a different revision.
300 directory; use -r/--rev to specify a different revision.
301
301
302 The archive type is automatically detected based on file
302 The archive type is automatically detected based on file
303 extension (or override using -t/--type).
303 extension (or override using -t/--type).
304
304
305 Valid types are:
305 Valid types are:
306
306
307 :``files``: a directory full of files (default)
307 :``files``: a directory full of files (default)
308 :``tar``: tar archive, uncompressed
308 :``tar``: tar archive, uncompressed
309 :``tbz2``: tar archive, compressed using bzip2
309 :``tbz2``: tar archive, compressed using bzip2
310 :``tgz``: tar archive, compressed using gzip
310 :``tgz``: tar archive, compressed using gzip
311 :``uzip``: zip archive, uncompressed
311 :``uzip``: zip archive, uncompressed
312 :``zip``: zip archive, compressed using deflate
312 :``zip``: zip archive, compressed using deflate
313
313
314 The exact name of the destination archive or directory is given
314 The exact name of the destination archive or directory is given
315 using a format string; see :hg:`help export` for details.
315 using a format string; see :hg:`help export` for details.
316
316
317 Each member added to an archive file has a directory prefix
317 Each member added to an archive file has a directory prefix
318 prepended. Use -p/--prefix to specify a format string for the
318 prepended. Use -p/--prefix to specify a format string for the
319 prefix. The default is the basename of the archive, with suffixes
319 prefix. The default is the basename of the archive, with suffixes
320 removed.
320 removed.
321
321
322 Returns 0 on success.
322 Returns 0 on success.
323 '''
323 '''
324
324
325 ctx = cmdutil.revsingle(repo, opts.get('rev'))
325 ctx = cmdutil.revsingle(repo, opts.get('rev'))
326 if not ctx:
326 if not ctx:
327 raise util.Abort(_('no working directory: please specify a revision'))
327 raise util.Abort(_('no working directory: please specify a revision'))
328 node = ctx.node()
328 node = ctx.node()
329 dest = cmdutil.makefilename(repo, dest, node)
329 dest = cmdutil.makefilename(repo, dest, node)
330 if os.path.realpath(dest) == repo.root:
330 if os.path.realpath(dest) == repo.root:
331 raise util.Abort(_('repository root cannot be destination'))
331 raise util.Abort(_('repository root cannot be destination'))
332
332
333 kind = opts.get('type') or archival.guesskind(dest) or 'files'
333 kind = opts.get('type') or archival.guesskind(dest) or 'files'
334 prefix = opts.get('prefix')
334 prefix = opts.get('prefix')
335
335
336 if dest == '-':
336 if dest == '-':
337 if kind == 'files':
337 if kind == 'files':
338 raise util.Abort(_('cannot archive plain files to stdout'))
338 raise util.Abort(_('cannot archive plain files to stdout'))
339 dest = sys.stdout
339 dest = sys.stdout
340 if not prefix:
340 if not prefix:
341 prefix = os.path.basename(repo.root) + '-%h'
341 prefix = os.path.basename(repo.root) + '-%h'
342
342
343 prefix = cmdutil.makefilename(repo, prefix, node)
343 prefix = cmdutil.makefilename(repo, prefix, node)
344 matchfn = cmdutil.match(repo, [], opts)
344 matchfn = cmdutil.match(repo, [], opts)
345 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
345 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
346 matchfn, prefix, subrepos=opts.get('subrepos'))
346 matchfn, prefix, subrepos=opts.get('subrepos'))
347
347
348 @command('backout',
348 @command('backout',
349 [('', 'merge', None, _('merge with old dirstate parent after backout')),
349 [('', 'merge', None, _('merge with old dirstate parent after backout')),
350 ('', 'parent', '', _('parent to choose when backing out merge'), _('REV')),
350 ('', 'parent', '', _('parent to choose when backing out merge'), _('REV')),
351 ('t', 'tool', '', _('specify merge tool')),
351 ('t', 'tool', '', _('specify merge tool')),
352 ('r', 'rev', '', _('revision to backout'), _('REV')),
352 ('r', 'rev', '', _('revision to backout'), _('REV')),
353 ] + walkopts + commitopts + commitopts2,
353 ] + walkopts + commitopts + commitopts2,
354 _('[OPTION]... [-r] REV'))
354 _('[OPTION]... [-r] REV'))
355 def backout(ui, repo, node=None, rev=None, **opts):
355 def backout(ui, repo, node=None, rev=None, **opts):
356 '''reverse effect of earlier changeset
356 '''reverse effect of earlier changeset
357
357
358 Prepare a new changeset with the effect of REV undone in the
358 Prepare a new changeset with the effect of REV undone in the
359 current working directory.
359 current working directory.
360
360
361 If REV is the parent of the working directory, then this new changeset
361 If REV is the parent of the working directory, then this new changeset
362 is committed automatically. Otherwise, hg needs to merge the
362 is committed automatically. Otherwise, hg needs to merge the
363 changes and the merged result is left uncommitted.
363 changes and the merged result is left uncommitted.
364
364
365 By default, the pending changeset will have one parent,
365 By default, the pending changeset will have one parent,
366 maintaining a linear history. With --merge, the pending changeset
366 maintaining a linear history. With --merge, the pending changeset
367 will instead have two parents: the old parent of the working
367 will instead have two parents: the old parent of the working
368 directory and a new child of REV that simply undoes REV.
368 directory and a new child of REV that simply undoes REV.
369
369
370 Before version 1.7, the behavior without --merge was equivalent to
370 Before version 1.7, the behavior without --merge was equivalent to
371 specifying --merge followed by :hg:`update --clean .` to cancel
371 specifying --merge followed by :hg:`update --clean .` to cancel
372 the merge and leave the child of REV as a head to be merged
372 the merge and leave the child of REV as a head to be merged
373 separately.
373 separately.
374
374
375 See :hg:`help dates` for a list of formats valid for -d/--date.
375 See :hg:`help dates` for a list of formats valid for -d/--date.
376
376
377 Returns 0 on success.
377 Returns 0 on success.
378 '''
378 '''
379 if rev and node:
379 if rev and node:
380 raise util.Abort(_("please specify just one revision"))
380 raise util.Abort(_("please specify just one revision"))
381
381
382 if not rev:
382 if not rev:
383 rev = node
383 rev = node
384
384
385 if not rev:
385 if not rev:
386 raise util.Abort(_("please specify a revision to backout"))
386 raise util.Abort(_("please specify a revision to backout"))
387
387
388 date = opts.get('date')
388 date = opts.get('date')
389 if date:
389 if date:
390 opts['date'] = util.parsedate(date)
390 opts['date'] = util.parsedate(date)
391
391
392 cmdutil.bailifchanged(repo)
392 cmdutil.bailifchanged(repo)
393 node = cmdutil.revsingle(repo, rev).node()
393 node = cmdutil.revsingle(repo, rev).node()
394
394
395 op1, op2 = repo.dirstate.parents()
395 op1, op2 = repo.dirstate.parents()
396 a = repo.changelog.ancestor(op1, node)
396 a = repo.changelog.ancestor(op1, node)
397 if a != node:
397 if a != node:
398 raise util.Abort(_('cannot backout change on a different branch'))
398 raise util.Abort(_('cannot backout change on a different branch'))
399
399
400 p1, p2 = repo.changelog.parents(node)
400 p1, p2 = repo.changelog.parents(node)
401 if p1 == nullid:
401 if p1 == nullid:
402 raise util.Abort(_('cannot backout a change with no parents'))
402 raise util.Abort(_('cannot backout a change with no parents'))
403 if p2 != nullid:
403 if p2 != nullid:
404 if not opts.get('parent'):
404 if not opts.get('parent'):
405 raise util.Abort(_('cannot backout a merge changeset without '
405 raise util.Abort(_('cannot backout a merge changeset without '
406 '--parent'))
406 '--parent'))
407 p = repo.lookup(opts['parent'])
407 p = repo.lookup(opts['parent'])
408 if p not in (p1, p2):
408 if p not in (p1, p2):
409 raise util.Abort(_('%s is not a parent of %s') %
409 raise util.Abort(_('%s is not a parent of %s') %
410 (short(p), short(node)))
410 (short(p), short(node)))
411 parent = p
411 parent = p
412 else:
412 else:
413 if opts.get('parent'):
413 if opts.get('parent'):
414 raise util.Abort(_('cannot use --parent on non-merge changeset'))
414 raise util.Abort(_('cannot use --parent on non-merge changeset'))
415 parent = p1
415 parent = p1
416
416
417 # the backout should appear on the same branch
417 # the backout should appear on the same branch
418 branch = repo.dirstate.branch()
418 branch = repo.dirstate.branch()
419 hg.clean(repo, node, show_stats=False)
419 hg.clean(repo, node, show_stats=False)
420 repo.dirstate.setbranch(branch)
420 repo.dirstate.setbranch(branch)
421 revert_opts = opts.copy()
421 revert_opts = opts.copy()
422 revert_opts['date'] = None
422 revert_opts['date'] = None
423 revert_opts['all'] = True
423 revert_opts['all'] = True
424 revert_opts['rev'] = hex(parent)
424 revert_opts['rev'] = hex(parent)
425 revert_opts['no_backup'] = None
425 revert_opts['no_backup'] = None
426 revert(ui, repo, **revert_opts)
426 revert(ui, repo, **revert_opts)
427 if not opts.get('merge') and op1 != node:
427 if not opts.get('merge') and op1 != node:
428 try:
428 try:
429 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
429 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
430 return hg.update(repo, op1)
430 return hg.update(repo, op1)
431 finally:
431 finally:
432 ui.setconfig('ui', 'forcemerge', '')
432 ui.setconfig('ui', 'forcemerge', '')
433
433
434 commit_opts = opts.copy()
434 commit_opts = opts.copy()
435 commit_opts['addremove'] = False
435 commit_opts['addremove'] = False
436 if not commit_opts['message'] and not commit_opts['logfile']:
436 if not commit_opts['message'] and not commit_opts['logfile']:
437 # we don't translate commit messages
437 # we don't translate commit messages
438 commit_opts['message'] = "Backed out changeset %s" % short(node)
438 commit_opts['message'] = "Backed out changeset %s" % short(node)
439 commit_opts['force_editor'] = True
439 commit_opts['force_editor'] = True
440 commit(ui, repo, **commit_opts)
440 commit(ui, repo, **commit_opts)
441 def nice(node):
441 def nice(node):
442 return '%d:%s' % (repo.changelog.rev(node), short(node))
442 return '%d:%s' % (repo.changelog.rev(node), short(node))
443 ui.status(_('changeset %s backs out changeset %s\n') %
443 ui.status(_('changeset %s backs out changeset %s\n') %
444 (nice(repo.changelog.tip()), nice(node)))
444 (nice(repo.changelog.tip()), nice(node)))
445 if opts.get('merge') and op1 != node:
445 if opts.get('merge') and op1 != node:
446 hg.clean(repo, op1, show_stats=False)
446 hg.clean(repo, op1, show_stats=False)
447 ui.status(_('merging with changeset %s\n')
447 ui.status(_('merging with changeset %s\n')
448 % nice(repo.changelog.tip()))
448 % nice(repo.changelog.tip()))
449 try:
449 try:
450 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
450 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
451 return hg.merge(repo, hex(repo.changelog.tip()))
451 return hg.merge(repo, hex(repo.changelog.tip()))
452 finally:
452 finally:
453 ui.setconfig('ui', 'forcemerge', '')
453 ui.setconfig('ui', 'forcemerge', '')
454 return 0
454 return 0
455
455
456 @command('bisect',
456 @command('bisect',
457 [('r', 'reset', False, _('reset bisect state')),
457 [('r', 'reset', False, _('reset bisect state')),
458 ('g', 'good', False, _('mark changeset good')),
458 ('g', 'good', False, _('mark changeset good')),
459 ('b', 'bad', False, _('mark changeset bad')),
459 ('b', 'bad', False, _('mark changeset bad')),
460 ('s', 'skip', False, _('skip testing changeset')),
460 ('s', 'skip', False, _('skip testing changeset')),
461 ('e', 'extend', False, _('extend the bisect range')),
461 ('e', 'extend', False, _('extend the bisect range')),
462 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
462 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
463 ('U', 'noupdate', False, _('do not update to target'))],
463 ('U', 'noupdate', False, _('do not update to target'))],
464 _("[-gbsr] [-U] [-c CMD] [REV]"))
464 _("[-gbsr] [-U] [-c CMD] [REV]"))
465 def bisect(ui, repo, rev=None, extra=None, command=None,
465 def bisect(ui, repo, rev=None, extra=None, command=None,
466 reset=None, good=None, bad=None, skip=None, extend=None,
466 reset=None, good=None, bad=None, skip=None, extend=None,
467 noupdate=None):
467 noupdate=None):
468 """subdivision search of changesets
468 """subdivision search of changesets
469
469
470 This command helps to find changesets which introduce problems. To
470 This command helps to find changesets which introduce problems. To
471 use, mark the earliest changeset you know exhibits the problem as
471 use, mark the earliest changeset you know exhibits the problem as
472 bad, then mark the latest changeset which is free from the problem
472 bad, then mark the latest changeset which is free from the problem
473 as good. Bisect will update your working directory to a revision
473 as good. Bisect will update your working directory to a revision
474 for testing (unless the -U/--noupdate option is specified). Once
474 for testing (unless the -U/--noupdate option is specified). Once
475 you have performed tests, mark the working directory as good or
475 you have performed tests, mark the working directory as good or
476 bad, and bisect will either update to another candidate changeset
476 bad, and bisect will either update to another candidate changeset
477 or announce that it has found the bad revision.
477 or announce that it has found the bad revision.
478
478
479 As a shortcut, you can also use the revision argument to mark a
479 As a shortcut, you can also use the revision argument to mark a
480 revision as good or bad without checking it out first.
480 revision as good or bad without checking it out first.
481
481
482 If you supply a command, it will be used for automatic bisection.
482 If you supply a command, it will be used for automatic bisection.
483 Its exit status will be used to mark revisions as good or bad:
483 Its exit status will be used to mark revisions as good or bad:
484 status 0 means good, 125 means to skip the revision, 127
484 status 0 means good, 125 means to skip the revision, 127
485 (command not found) will abort the bisection, and any other
485 (command not found) will abort the bisection, and any other
486 non-zero exit status means the revision is bad.
486 non-zero exit status means the revision is bad.
487
487
488 Returns 0 on success.
488 Returns 0 on success.
489 """
489 """
490 def extendbisectrange(nodes, good):
490 def extendbisectrange(nodes, good):
491 # bisect is incomplete when it ends on a merge node and
491 # bisect is incomplete when it ends on a merge node and
492 # one of the parent was not checked.
492 # one of the parent was not checked.
493 parents = repo[nodes[0]].parents()
493 parents = repo[nodes[0]].parents()
494 if len(parents) > 1:
494 if len(parents) > 1:
495 side = good and state['bad'] or state['good']
495 side = good and state['bad'] or state['good']
496 num = len(set(i.node() for i in parents) & set(side))
496 num = len(set(i.node() for i in parents) & set(side))
497 if num == 1:
497 if num == 1:
498 return parents[0].ancestor(parents[1])
498 return parents[0].ancestor(parents[1])
499 return None
499 return None
500
500
501 def print_result(nodes, good):
501 def print_result(nodes, good):
502 displayer = cmdutil.show_changeset(ui, repo, {})
502 displayer = cmdutil.show_changeset(ui, repo, {})
503 if len(nodes) == 1:
503 if len(nodes) == 1:
504 # narrowed it down to a single revision
504 # narrowed it down to a single revision
505 if good:
505 if good:
506 ui.write(_("The first good revision is:\n"))
506 ui.write(_("The first good revision is:\n"))
507 else:
507 else:
508 ui.write(_("The first bad revision is:\n"))
508 ui.write(_("The first bad revision is:\n"))
509 displayer.show(repo[nodes[0]])
509 displayer.show(repo[nodes[0]])
510 extendnode = extendbisectrange(nodes, good)
510 extendnode = extendbisectrange(nodes, good)
511 if extendnode is not None:
511 if extendnode is not None:
512 ui.write(_('Not all ancestors of this changeset have been'
512 ui.write(_('Not all ancestors of this changeset have been'
513 ' checked.\nUse bisect --extend to continue the '
513 ' checked.\nUse bisect --extend to continue the '
514 'bisection from\nthe common ancestor, %s.\n')
514 'bisection from\nthe common ancestor, %s.\n')
515 % extendnode)
515 % extendnode)
516 else:
516 else:
517 # multiple possible revisions
517 # multiple possible revisions
518 if good:
518 if good:
519 ui.write(_("Due to skipped revisions, the first "
519 ui.write(_("Due to skipped revisions, the first "
520 "good revision could be any of:\n"))
520 "good revision could be any of:\n"))
521 else:
521 else:
522 ui.write(_("Due to skipped revisions, the first "
522 ui.write(_("Due to skipped revisions, the first "
523 "bad revision could be any of:\n"))
523 "bad revision could be any of:\n"))
524 for n in nodes:
524 for n in nodes:
525 displayer.show(repo[n])
525 displayer.show(repo[n])
526 displayer.close()
526 displayer.close()
527
527
528 def check_state(state, interactive=True):
528 def check_state(state, interactive=True):
529 if not state['good'] or not state['bad']:
529 if not state['good'] or not state['bad']:
530 if (good or bad or skip or reset) and interactive:
530 if (good or bad or skip or reset) and interactive:
531 return
531 return
532 if not state['good']:
532 if not state['good']:
533 raise util.Abort(_('cannot bisect (no known good revisions)'))
533 raise util.Abort(_('cannot bisect (no known good revisions)'))
534 else:
534 else:
535 raise util.Abort(_('cannot bisect (no known bad revisions)'))
535 raise util.Abort(_('cannot bisect (no known bad revisions)'))
536 return True
536 return True
537
537
538 # backward compatibility
538 # backward compatibility
539 if rev in "good bad reset init".split():
539 if rev in "good bad reset init".split():
540 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
540 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
541 cmd, rev, extra = rev, extra, None
541 cmd, rev, extra = rev, extra, None
542 if cmd == "good":
542 if cmd == "good":
543 good = True
543 good = True
544 elif cmd == "bad":
544 elif cmd == "bad":
545 bad = True
545 bad = True
546 else:
546 else:
547 reset = True
547 reset = True
548 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
548 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
549 raise util.Abort(_('incompatible arguments'))
549 raise util.Abort(_('incompatible arguments'))
550
550
551 if reset:
551 if reset:
552 p = repo.join("bisect.state")
552 p = repo.join("bisect.state")
553 if os.path.exists(p):
553 if os.path.exists(p):
554 os.unlink(p)
554 os.unlink(p)
555 return
555 return
556
556
557 state = hbisect.load_state(repo)
557 state = hbisect.load_state(repo)
558
558
559 if command:
559 if command:
560 changesets = 1
560 changesets = 1
561 try:
561 try:
562 while changesets:
562 while changesets:
563 # update state
563 # update state
564 status = util.system(command)
564 status = util.system(command)
565 if status == 125:
565 if status == 125:
566 transition = "skip"
566 transition = "skip"
567 elif status == 0:
567 elif status == 0:
568 transition = "good"
568 transition = "good"
569 # status < 0 means process was killed
569 # status < 0 means process was killed
570 elif status == 127:
570 elif status == 127:
571 raise util.Abort(_("failed to execute %s") % command)
571 raise util.Abort(_("failed to execute %s") % command)
572 elif status < 0:
572 elif status < 0:
573 raise util.Abort(_("%s killed") % command)
573 raise util.Abort(_("%s killed") % command)
574 else:
574 else:
575 transition = "bad"
575 transition = "bad"
576 ctx = cmdutil.revsingle(repo, rev)
576 ctx = cmdutil.revsingle(repo, rev)
577 rev = None # clear for future iterations
577 rev = None # clear for future iterations
578 state[transition].append(ctx.node())
578 state[transition].append(ctx.node())
579 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
579 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
580 check_state(state, interactive=False)
580 check_state(state, interactive=False)
581 # bisect
581 # bisect
582 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
582 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
583 # update to next check
583 # update to next check
584 cmdutil.bailifchanged(repo)
584 cmdutil.bailifchanged(repo)
585 hg.clean(repo, nodes[0], show_stats=False)
585 hg.clean(repo, nodes[0], show_stats=False)
586 finally:
586 finally:
587 hbisect.save_state(repo, state)
587 hbisect.save_state(repo, state)
588 print_result(nodes, good)
588 print_result(nodes, good)
589 return
589 return
590
590
591 # update state
591 # update state
592
592
593 if rev:
593 if rev:
594 nodes = [repo.lookup(i) for i in cmdutil.revrange(repo, [rev])]
594 nodes = [repo.lookup(i) for i in cmdutil.revrange(repo, [rev])]
595 else:
595 else:
596 nodes = [repo.lookup('.')]
596 nodes = [repo.lookup('.')]
597
597
598 if good or bad or skip:
598 if good or bad or skip:
599 if good:
599 if good:
600 state['good'] += nodes
600 state['good'] += nodes
601 elif bad:
601 elif bad:
602 state['bad'] += nodes
602 state['bad'] += nodes
603 elif skip:
603 elif skip:
604 state['skip'] += nodes
604 state['skip'] += nodes
605 hbisect.save_state(repo, state)
605 hbisect.save_state(repo, state)
606
606
607 if not check_state(state):
607 if not check_state(state):
608 return
608 return
609
609
610 # actually bisect
610 # actually bisect
611 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
611 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
612 if extend:
612 if extend:
613 if not changesets:
613 if not changesets:
614 extendnode = extendbisectrange(nodes, good)
614 extendnode = extendbisectrange(nodes, good)
615 if extendnode is not None:
615 if extendnode is not None:
616 ui.write(_("Extending search to changeset %d:%s\n"
616 ui.write(_("Extending search to changeset %d:%s\n"
617 % (extendnode.rev(), extendnode)))
617 % (extendnode.rev(), extendnode)))
618 if noupdate:
618 if noupdate:
619 return
619 return
620 cmdutil.bailifchanged(repo)
620 cmdutil.bailifchanged(repo)
621 return hg.clean(repo, extendnode.node())
621 return hg.clean(repo, extendnode.node())
622 raise util.Abort(_("nothing to extend"))
622 raise util.Abort(_("nothing to extend"))
623
623
624 if changesets == 0:
624 if changesets == 0:
625 print_result(nodes, good)
625 print_result(nodes, good)
626 else:
626 else:
627 assert len(nodes) == 1 # only a single node can be tested next
627 assert len(nodes) == 1 # only a single node can be tested next
628 node = nodes[0]
628 node = nodes[0]
629 # compute the approximate number of remaining tests
629 # compute the approximate number of remaining tests
630 tests, size = 0, 2
630 tests, size = 0, 2
631 while size <= changesets:
631 while size <= changesets:
632 tests, size = tests + 1, size * 2
632 tests, size = tests + 1, size * 2
633 rev = repo.changelog.rev(node)
633 rev = repo.changelog.rev(node)
634 ui.write(_("Testing changeset %d:%s "
634 ui.write(_("Testing changeset %d:%s "
635 "(%d changesets remaining, ~%d tests)\n")
635 "(%d changesets remaining, ~%d tests)\n")
636 % (rev, short(node), changesets, tests))
636 % (rev, short(node), changesets, tests))
637 if not noupdate:
637 if not noupdate:
638 cmdutil.bailifchanged(repo)
638 cmdutil.bailifchanged(repo)
639 return hg.clean(repo, node)
639 return hg.clean(repo, node)
640
640
641 @command('bookmarks',
641 @command('bookmarks',
642 [('f', 'force', False, _('force')),
642 [('f', 'force', False, _('force')),
643 ('r', 'rev', '', _('revision'), _('REV')),
643 ('r', 'rev', '', _('revision'), _('REV')),
644 ('d', 'delete', False, _('delete a given bookmark')),
644 ('d', 'delete', False, _('delete a given bookmark')),
645 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
645 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
646 ('i', 'inactive', False, _('do not mark a new bookmark active'))],
646 ('i', 'inactive', False, _('do not mark a new bookmark active'))],
647 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
647 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
648 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
648 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
649 rename=None, inactive=False):
649 rename=None, inactive=False):
650 '''track a line of development with movable markers
650 '''track a line of development with movable markers
651
651
652 Bookmarks are pointers to certain commits that move when
652 Bookmarks are pointers to certain commits that move when
653 committing. Bookmarks are local. They can be renamed, copied and
653 committing. Bookmarks are local. They can be renamed, copied and
654 deleted. It is possible to use bookmark names in :hg:`merge` and
654 deleted. It is possible to use bookmark names in :hg:`merge` and
655 :hg:`update` to merge and update respectively to a given bookmark.
655 :hg:`update` to merge and update respectively to a given bookmark.
656
656
657 You can use :hg:`bookmark NAME` to set a bookmark on the working
657 You can use :hg:`bookmark NAME` to set a bookmark on the working
658 directory's parent revision with the given name. If you specify
658 directory's parent revision with the given name. If you specify
659 a revision using -r REV (where REV may be an existing bookmark),
659 a revision using -r REV (where REV may be an existing bookmark),
660 the bookmark is assigned to that revision.
660 the bookmark is assigned to that revision.
661
661
662 Bookmarks can be pushed and pulled between repositories (see :hg:`help
662 Bookmarks can be pushed and pulled between repositories (see :hg:`help
663 push` and :hg:`help pull`). This requires both the local and remote
663 push` and :hg:`help pull`). This requires both the local and remote
664 repositories to support bookmarks. For versions prior to 1.8, this means
664 repositories to support bookmarks. For versions prior to 1.8, this means
665 the bookmarks extension must be enabled.
665 the bookmarks extension must be enabled.
666 '''
666 '''
667 hexfn = ui.debugflag and hex or short
667 hexfn = ui.debugflag and hex or short
668 marks = repo._bookmarks
668 marks = repo._bookmarks
669 cur = repo.changectx('.').node()
669 cur = repo.changectx('.').node()
670
670
671 if rename:
671 if rename:
672 if rename not in marks:
672 if rename not in marks:
673 raise util.Abort(_("bookmark '%s' does not exist") % rename)
673 raise util.Abort(_("bookmark '%s' does not exist") % rename)
674 if mark in marks and not force:
674 if mark in marks and not force:
675 raise util.Abort(_("bookmark '%s' already exists "
675 raise util.Abort(_("bookmark '%s' already exists "
676 "(use -f to force)") % mark)
676 "(use -f to force)") % mark)
677 if mark is None:
677 if mark is None:
678 raise util.Abort(_("new bookmark name required"))
678 raise util.Abort(_("new bookmark name required"))
679 marks[mark] = marks[rename]
679 marks[mark] = marks[rename]
680 if repo._bookmarkcurrent == rename and not inactive:
680 if repo._bookmarkcurrent == rename and not inactive:
681 bookmarks.setcurrent(repo, mark)
681 bookmarks.setcurrent(repo, mark)
682 del marks[rename]
682 del marks[rename]
683 bookmarks.write(repo)
683 bookmarks.write(repo)
684 return
684 return
685
685
686 if delete:
686 if delete:
687 if mark is None:
687 if mark is None:
688 raise util.Abort(_("bookmark name required"))
688 raise util.Abort(_("bookmark name required"))
689 if mark not in marks:
689 if mark not in marks:
690 raise util.Abort(_("bookmark '%s' does not exist") % mark)
690 raise util.Abort(_("bookmark '%s' does not exist") % mark)
691 if mark == repo._bookmarkcurrent:
691 if mark == repo._bookmarkcurrent:
692 bookmarks.setcurrent(repo, None)
692 bookmarks.setcurrent(repo, None)
693 del marks[mark]
693 del marks[mark]
694 bookmarks.write(repo)
694 bookmarks.write(repo)
695 return
695 return
696
696
697 if mark is not None:
697 if mark is not None:
698 if "\n" in mark:
698 if "\n" in mark:
699 raise util.Abort(_("bookmark name cannot contain newlines"))
699 raise util.Abort(_("bookmark name cannot contain newlines"))
700 mark = mark.strip()
700 mark = mark.strip()
701 if not mark:
701 if not mark:
702 raise util.Abort(_("bookmark names cannot consist entirely of "
702 raise util.Abort(_("bookmark names cannot consist entirely of "
703 "whitespace"))
703 "whitespace"))
704 if inactive and mark == repo._bookmarkcurrent:
704 if inactive and mark == repo._bookmarkcurrent:
705 bookmarks.setcurrent(repo, None)
705 bookmarks.setcurrent(repo, None)
706 return
706 return
707 if mark in marks and not force:
707 if mark in marks and not force:
708 raise util.Abort(_("bookmark '%s' already exists "
708 raise util.Abort(_("bookmark '%s' already exists "
709 "(use -f to force)") % mark)
709 "(use -f to force)") % mark)
710 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
710 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
711 and not force):
711 and not force):
712 raise util.Abort(
712 raise util.Abort(
713 _("a bookmark cannot have the name of an existing branch"))
713 _("a bookmark cannot have the name of an existing branch"))
714 if rev:
714 if rev:
715 marks[mark] = repo.lookup(rev)
715 marks[mark] = repo.lookup(rev)
716 else:
716 else:
717 marks[mark] = repo.changectx('.').node()
717 marks[mark] = repo.changectx('.').node()
718 if not inactive and repo.changectx('.').node() == marks[mark]:
718 if not inactive and repo.changectx('.').node() == marks[mark]:
719 bookmarks.setcurrent(repo, mark)
719 bookmarks.setcurrent(repo, mark)
720 bookmarks.write(repo)
720 bookmarks.write(repo)
721 return
721 return
722
722
723 if mark is None:
723 if mark is None:
724 if rev:
724 if rev:
725 raise util.Abort(_("bookmark name required"))
725 raise util.Abort(_("bookmark name required"))
726 if len(marks) == 0:
726 if len(marks) == 0:
727 ui.status(_("no bookmarks set\n"))
727 ui.status(_("no bookmarks set\n"))
728 else:
728 else:
729 for bmark, n in sorted(marks.iteritems()):
729 for bmark, n in sorted(marks.iteritems()):
730 current = repo._bookmarkcurrent
730 current = repo._bookmarkcurrent
731 if bmark == current and n == cur:
731 if bmark == current and n == cur:
732 prefix, label = '*', 'bookmarks.current'
732 prefix, label = '*', 'bookmarks.current'
733 else:
733 else:
734 prefix, label = ' ', ''
734 prefix, label = ' ', ''
735
735
736 if ui.quiet:
736 if ui.quiet:
737 ui.write("%s\n" % bmark, label=label)
737 ui.write("%s\n" % bmark, label=label)
738 else:
738 else:
739 ui.write(" %s %-25s %d:%s\n" % (
739 ui.write(" %s %-25s %d:%s\n" % (
740 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
740 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
741 label=label)
741 label=label)
742 return
742 return
743
743
744 @command('branch',
744 @command('branch',
745 [('f', 'force', None,
745 [('f', 'force', None,
746 _('set branch name even if it shadows an existing branch')),
746 _('set branch name even if it shadows an existing branch')),
747 ('C', 'clean', None, _('reset branch name to parent branch name'))],
747 ('C', 'clean', None, _('reset branch name to parent branch name'))],
748 _('[-fC] [NAME]'))
748 _('[-fC] [NAME]'))
749 def branch(ui, repo, label=None, **opts):
749 def branch(ui, repo, label=None, **opts):
750 """set or show the current branch name
750 """set or show the current branch name
751
751
752 With no argument, show the current branch name. With one argument,
752 With no argument, show the current branch name. With one argument,
753 set the working directory branch name (the branch will not exist
753 set the working directory branch name (the branch will not exist
754 in the repository until the next commit). Standard practice
754 in the repository until the next commit). Standard practice
755 recommends that primary development take place on the 'default'
755 recommends that primary development take place on the 'default'
756 branch.
756 branch.
757
757
758 Unless -f/--force is specified, branch will not let you set a
758 Unless -f/--force is specified, branch will not let you set a
759 branch name that already exists, even if it's inactive.
759 branch name that already exists, even if it's inactive.
760
760
761 Use -C/--clean to reset the working directory branch to that of
761 Use -C/--clean to reset the working directory branch to that of
762 the parent of the working directory, negating a previous branch
762 the parent of the working directory, negating a previous branch
763 change.
763 change.
764
764
765 Use the command :hg:`update` to switch to an existing branch. Use
765 Use the command :hg:`update` to switch to an existing branch. Use
766 :hg:`commit --close-branch` to mark this branch as closed.
766 :hg:`commit --close-branch` to mark this branch as closed.
767
767
768 Returns 0 on success.
768 Returns 0 on success.
769 """
769 """
770
770
771 if opts.get('clean'):
771 if opts.get('clean'):
772 label = repo[None].p1().branch()
772 label = repo[None].p1().branch()
773 repo.dirstate.setbranch(label)
773 repo.dirstate.setbranch(label)
774 ui.status(_('reset working directory to branch %s\n') % label)
774 ui.status(_('reset working directory to branch %s\n') % label)
775 elif label:
775 elif label:
776 if not opts.get('force') and label in repo.branchtags():
776 if not opts.get('force') and label in repo.branchtags():
777 if label not in [p.branch() for p in repo.parents()]:
777 if label not in [p.branch() for p in repo.parents()]:
778 raise util.Abort(_('a branch of the same name already exists'),
778 raise util.Abort(_('a branch of the same name already exists'),
779 # i18n: "it" refers to an existing branch
779 # i18n: "it" refers to an existing branch
780 hint=_("use 'hg update' to switch to it"))
780 hint=_("use 'hg update' to switch to it"))
781 repo.dirstate.setbranch(label)
781 repo.dirstate.setbranch(label)
782 ui.status(_('marked working directory as branch %s\n') % label)
782 ui.status(_('marked working directory as branch %s\n') % label)
783 else:
783 else:
784 ui.write("%s\n" % repo.dirstate.branch())
784 ui.write("%s\n" % repo.dirstate.branch())
785
785
786 @command('branches',
786 @command('branches',
787 [('a', 'active', False, _('show only branches that have unmerged heads')),
787 [('a', 'active', False, _('show only branches that have unmerged heads')),
788 ('c', 'closed', False, _('show normal and closed branches'))],
788 ('c', 'closed', False, _('show normal and closed branches'))],
789 _('[-ac]'))
789 _('[-ac]'))
790 def branches(ui, repo, active=False, closed=False):
790 def branches(ui, repo, active=False, closed=False):
791 """list repository named branches
791 """list repository named branches
792
792
793 List the repository's named branches, indicating which ones are
793 List the repository's named branches, indicating which ones are
794 inactive. If -c/--closed is specified, also list branches which have
794 inactive. If -c/--closed is specified, also list branches which have
795 been marked closed (see :hg:`commit --close-branch`).
795 been marked closed (see :hg:`commit --close-branch`).
796
796
797 If -a/--active is specified, only show active branches. A branch
797 If -a/--active is specified, only show active branches. A branch
798 is considered active if it contains repository heads.
798 is considered active if it contains repository heads.
799
799
800 Use the command :hg:`update` to switch to an existing branch.
800 Use the command :hg:`update` to switch to an existing branch.
801
801
802 Returns 0.
802 Returns 0.
803 """
803 """
804
804
805 hexfunc = ui.debugflag and hex or short
805 hexfunc = ui.debugflag and hex or short
806 activebranches = [repo[n].branch() for n in repo.heads()]
806 activebranches = [repo[n].branch() for n in repo.heads()]
807 def testactive(tag, node):
807 def testactive(tag, node):
808 realhead = tag in activebranches
808 realhead = tag in activebranches
809 open = node in repo.branchheads(tag, closed=False)
809 open = node in repo.branchheads(tag, closed=False)
810 return realhead and open
810 return realhead and open
811 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
811 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
812 for tag, node in repo.branchtags().items()],
812 for tag, node in repo.branchtags().items()],
813 reverse=True)
813 reverse=True)
814
814
815 for isactive, node, tag in branches:
815 for isactive, node, tag in branches:
816 if (not active) or isactive:
816 if (not active) or isactive:
817 if ui.quiet:
817 if ui.quiet:
818 ui.write("%s\n" % tag)
818 ui.write("%s\n" % tag)
819 else:
819 else:
820 hn = repo.lookup(node)
820 hn = repo.lookup(node)
821 if isactive:
821 if isactive:
822 label = 'branches.active'
822 label = 'branches.active'
823 notice = ''
823 notice = ''
824 elif hn not in repo.branchheads(tag, closed=False):
824 elif hn not in repo.branchheads(tag, closed=False):
825 if not closed:
825 if not closed:
826 continue
826 continue
827 label = 'branches.closed'
827 label = 'branches.closed'
828 notice = _(' (closed)')
828 notice = _(' (closed)')
829 else:
829 else:
830 label = 'branches.inactive'
830 label = 'branches.inactive'
831 notice = _(' (inactive)')
831 notice = _(' (inactive)')
832 if tag == repo.dirstate.branch():
832 if tag == repo.dirstate.branch():
833 label = 'branches.current'
833 label = 'branches.current'
834 rev = str(node).rjust(31 - encoding.colwidth(tag))
834 rev = str(node).rjust(31 - encoding.colwidth(tag))
835 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
835 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
836 tag = ui.label(tag, label)
836 tag = ui.label(tag, label)
837 ui.write("%s %s%s\n" % (tag, rev, notice))
837 ui.write("%s %s%s\n" % (tag, rev, notice))
838
838
839 @command('bundle',
839 @command('bundle',
840 [('f', 'force', None, _('run even when the destination is unrelated')),
840 [('f', 'force', None, _('run even when the destination is unrelated')),
841 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
841 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
842 _('REV')),
842 _('REV')),
843 ('b', 'branch', [], _('a specific branch you would like to bundle'),
843 ('b', 'branch', [], _('a specific branch you would like to bundle'),
844 _('BRANCH')),
844 _('BRANCH')),
845 ('', 'base', [],
845 ('', 'base', [],
846 _('a base changeset assumed to be available at the destination'),
846 _('a base changeset assumed to be available at the destination'),
847 _('REV')),
847 _('REV')),
848 ('a', 'all', None, _('bundle all changesets in the repository')),
848 ('a', 'all', None, _('bundle all changesets in the repository')),
849 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
849 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
850 ] + remoteopts,
850 ] + remoteopts,
851 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
851 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
852 def bundle(ui, repo, fname, dest=None, **opts):
852 def bundle(ui, repo, fname, dest=None, **opts):
853 """create a changegroup file
853 """create a changegroup file
854
854
855 Generate a compressed changegroup file collecting changesets not
855 Generate a compressed changegroup file collecting changesets not
856 known to be in another repository.
856 known to be in another repository.
857
857
858 If you omit the destination repository, then hg assumes the
858 If you omit the destination repository, then hg assumes the
859 destination will have all the nodes you specify with --base
859 destination will have all the nodes you specify with --base
860 parameters. To create a bundle containing all changesets, use
860 parameters. To create a bundle containing all changesets, use
861 -a/--all (or --base null).
861 -a/--all (or --base null).
862
862
863 You can change compression method with the -t/--type option.
863 You can change compression method with the -t/--type option.
864 The available compression methods are: none, bzip2, and
864 The available compression methods are: none, bzip2, and
865 gzip (by default, bundles are compressed using bzip2).
865 gzip (by default, bundles are compressed using bzip2).
866
866
867 The bundle file can then be transferred using conventional means
867 The bundle file can then be transferred using conventional means
868 and applied to another repository with the unbundle or pull
868 and applied to another repository with the unbundle or pull
869 command. This is useful when direct push and pull are not
869 command. This is useful when direct push and pull are not
870 available or when exporting an entire repository is undesirable.
870 available or when exporting an entire repository is undesirable.
871
871
872 Applying bundles preserves all changeset contents including
872 Applying bundles preserves all changeset contents including
873 permissions, copy/rename information, and revision history.
873 permissions, copy/rename information, and revision history.
874
874
875 Returns 0 on success, 1 if no changes found.
875 Returns 0 on success, 1 if no changes found.
876 """
876 """
877 revs = None
877 revs = None
878 if 'rev' in opts:
878 if 'rev' in opts:
879 revs = cmdutil.revrange(repo, opts['rev'])
879 revs = cmdutil.revrange(repo, opts['rev'])
880
880
881 if opts.get('all'):
881 if opts.get('all'):
882 base = ['null']
882 base = ['null']
883 else:
883 else:
884 base = cmdutil.revrange(repo, opts.get('base'))
884 base = cmdutil.revrange(repo, opts.get('base'))
885 if base:
885 if base:
886 if dest:
886 if dest:
887 raise util.Abort(_("--base is incompatible with specifying "
887 raise util.Abort(_("--base is incompatible with specifying "
888 "a destination"))
888 "a destination"))
889 common = [repo.lookup(rev) for rev in base]
889 common = [repo.lookup(rev) for rev in base]
890 heads = revs and map(repo.lookup, revs) or revs
890 heads = revs and map(repo.lookup, revs) or revs
891 else:
891 else:
892 dest = ui.expandpath(dest or 'default-push', dest or 'default')
892 dest = ui.expandpath(dest or 'default-push', dest or 'default')
893 dest, branches = hg.parseurl(dest, opts.get('branch'))
893 dest, branches = hg.parseurl(dest, opts.get('branch'))
894 other = hg.repository(hg.remoteui(repo, opts), dest)
894 other = hg.repository(hg.remoteui(repo, opts), dest)
895 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
895 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
896 heads = revs and map(repo.lookup, revs) or revs
896 heads = revs and map(repo.lookup, revs) or revs
897 common, outheads = discovery.findcommonoutgoing(repo, other,
897 common, outheads = discovery.findcommonoutgoing(repo, other,
898 onlyheads=heads,
898 onlyheads=heads,
899 force=opts.get('force'))
899 force=opts.get('force'))
900
900
901 cg = repo.getbundle('bundle', common=common, heads=heads)
901 cg = repo.getbundle('bundle', common=common, heads=heads)
902 if not cg:
902 if not cg:
903 ui.status(_("no changes found\n"))
903 ui.status(_("no changes found\n"))
904 return 1
904 return 1
905
905
906 bundletype = opts.get('type', 'bzip2').lower()
906 bundletype = opts.get('type', 'bzip2').lower()
907 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
907 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
908 bundletype = btypes.get(bundletype)
908 bundletype = btypes.get(bundletype)
909 if bundletype not in changegroup.bundletypes:
909 if bundletype not in changegroup.bundletypes:
910 raise util.Abort(_('unknown bundle type specified with --type'))
910 raise util.Abort(_('unknown bundle type specified with --type'))
911
911
912 changegroup.writebundle(cg, fname, bundletype)
912 changegroup.writebundle(cg, fname, bundletype)
913
913
914 @command('cat',
914 @command('cat',
915 [('o', 'output', '',
915 [('o', 'output', '',
916 _('print output to file with formatted name'), _('FORMAT')),
916 _('print output to file with formatted name'), _('FORMAT')),
917 ('r', 'rev', '', _('print the given revision'), _('REV')),
917 ('r', 'rev', '', _('print the given revision'), _('REV')),
918 ('', 'decode', None, _('apply any matching decode filter')),
918 ('', 'decode', None, _('apply any matching decode filter')),
919 ] + walkopts,
919 ] + walkopts,
920 _('[OPTION]... FILE...'))
920 _('[OPTION]... FILE...'))
921 def cat(ui, repo, file1, *pats, **opts):
921 def cat(ui, repo, file1, *pats, **opts):
922 """output the current or given revision of files
922 """output the current or given revision of files
923
923
924 Print the specified files as they were at the given revision. If
924 Print the specified files as they were at the given revision. If
925 no revision is given, the parent of the working directory is used,
925 no revision is given, the parent of the working directory is used,
926 or tip if no revision is checked out.
926 or tip if no revision is checked out.
927
927
928 Output may be to a file, in which case the name of the file is
928 Output may be to a file, in which case the name of the file is
929 given using a format string. The formatting rules are the same as
929 given using a format string. The formatting rules are the same as
930 for the export command, with the following additions:
930 for the export command, with the following additions:
931
931
932 :``%s``: basename of file being printed
932 :``%s``: basename of file being printed
933 :``%d``: dirname of file being printed, or '.' if in repository root
933 :``%d``: dirname of file being printed, or '.' if in repository root
934 :``%p``: root-relative path name of file being printed
934 :``%p``: root-relative path name of file being printed
935
935
936 Returns 0 on success.
936 Returns 0 on success.
937 """
937 """
938 ctx = cmdutil.revsingle(repo, opts.get('rev'))
938 ctx = cmdutil.revsingle(repo, opts.get('rev'))
939 err = 1
939 err = 1
940 m = cmdutil.match(repo, (file1,) + pats, opts)
940 m = cmdutil.match(repo, (file1,) + pats, opts)
941 for abs in ctx.walk(m):
941 for abs in ctx.walk(m):
942 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
942 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
943 pathname=abs)
943 pathname=abs)
944 data = ctx[abs].data()
944 data = ctx[abs].data()
945 if opts.get('decode'):
945 if opts.get('decode'):
946 data = repo.wwritedata(abs, data)
946 data = repo.wwritedata(abs, data)
947 fp.write(data)
947 fp.write(data)
948 fp.close()
948 fp.close()
949 err = 0
949 err = 0
950 return err
950 return err
951
951
952 @command('^clone',
952 @command('^clone',
953 [('U', 'noupdate', None,
953 [('U', 'noupdate', None,
954 _('the clone will include an empty working copy (only a repository)')),
954 _('the clone will include an empty working copy (only a repository)')),
955 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
955 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
956 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
956 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
957 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
957 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
958 ('', 'pull', None, _('use pull protocol to copy metadata')),
958 ('', 'pull', None, _('use pull protocol to copy metadata')),
959 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
959 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
960 ] + remoteopts,
960 ] + remoteopts,
961 _('[OPTION]... SOURCE [DEST]'))
961 _('[OPTION]... SOURCE [DEST]'))
962 def clone(ui, source, dest=None, **opts):
962 def clone(ui, source, dest=None, **opts):
963 """make a copy of an existing repository
963 """make a copy of an existing repository
964
964
965 Create a copy of an existing repository in a new directory.
965 Create a copy of an existing repository in a new directory.
966
966
967 If no destination directory name is specified, it defaults to the
967 If no destination directory name is specified, it defaults to the
968 basename of the source.
968 basename of the source.
969
969
970 The location of the source is added to the new repository's
970 The location of the source is added to the new repository's
971 ``.hg/hgrc`` file, as the default to be used for future pulls.
971 ``.hg/hgrc`` file, as the default to be used for future pulls.
972
972
973 See :hg:`help urls` for valid source format details.
973 See :hg:`help urls` for valid source format details.
974
974
975 It is possible to specify an ``ssh://`` URL as the destination, but no
975 It is possible to specify an ``ssh://`` URL as the destination, but no
976 ``.hg/hgrc`` and working directory will be created on the remote side.
976 ``.hg/hgrc`` and working directory will be created on the remote side.
977 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
977 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
978
978
979 A set of changesets (tags, or branch names) to pull may be specified
979 A set of changesets (tags, or branch names) to pull may be specified
980 by listing each changeset (tag, or branch name) with -r/--rev.
980 by listing each changeset (tag, or branch name) with -r/--rev.
981 If -r/--rev is used, the cloned repository will contain only a subset
981 If -r/--rev is used, the cloned repository will contain only a subset
982 of the changesets of the source repository. Only the set of changesets
982 of the changesets of the source repository. Only the set of changesets
983 defined by all -r/--rev options (including all their ancestors)
983 defined by all -r/--rev options (including all their ancestors)
984 will be pulled into the destination repository.
984 will be pulled into the destination repository.
985 No subsequent changesets (including subsequent tags) will be present
985 No subsequent changesets (including subsequent tags) will be present
986 in the destination.
986 in the destination.
987
987
988 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
988 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
989 local source repositories.
989 local source repositories.
990
990
991 For efficiency, hardlinks are used for cloning whenever the source
991 For efficiency, hardlinks are used for cloning whenever the source
992 and destination are on the same filesystem (note this applies only
992 and destination are on the same filesystem (note this applies only
993 to the repository data, not to the working directory). Some
993 to the repository data, not to the working directory). Some
994 filesystems, such as AFS, implement hardlinking incorrectly, but
994 filesystems, such as AFS, implement hardlinking incorrectly, but
995 do not report errors. In these cases, use the --pull option to
995 do not report errors. In these cases, use the --pull option to
996 avoid hardlinking.
996 avoid hardlinking.
997
997
998 In some cases, you can clone repositories and the working directory
998 In some cases, you can clone repositories and the working directory
999 using full hardlinks with ::
999 using full hardlinks with ::
1000
1000
1001 $ cp -al REPO REPOCLONE
1001 $ cp -al REPO REPOCLONE
1002
1002
1003 This is the fastest way to clone, but it is not always safe. The
1003 This is the fastest way to clone, but it is not always safe. The
1004 operation is not atomic (making sure REPO is not modified during
1004 operation is not atomic (making sure REPO is not modified during
1005 the operation is up to you) and you have to make sure your editor
1005 the operation is up to you) and you have to make sure your editor
1006 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
1006 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
1007 this is not compatible with certain extensions that place their
1007 this is not compatible with certain extensions that place their
1008 metadata under the .hg directory, such as mq.
1008 metadata under the .hg directory, such as mq.
1009
1009
1010 Mercurial will update the working directory to the first applicable
1010 Mercurial will update the working directory to the first applicable
1011 revision from this list:
1011 revision from this list:
1012
1012
1013 a) null if -U or the source repository has no changesets
1013 a) null if -U or the source repository has no changesets
1014 b) if -u . and the source repository is local, the first parent of
1014 b) if -u . and the source repository is local, the first parent of
1015 the source repository's working directory
1015 the source repository's working directory
1016 c) the changeset specified with -u (if a branch name, this means the
1016 c) the changeset specified with -u (if a branch name, this means the
1017 latest head of that branch)
1017 latest head of that branch)
1018 d) the changeset specified with -r
1018 d) the changeset specified with -r
1019 e) the tipmost head specified with -b
1019 e) the tipmost head specified with -b
1020 f) the tipmost head specified with the url#branch source syntax
1020 f) the tipmost head specified with the url#branch source syntax
1021 g) the tipmost head of the default branch
1021 g) the tipmost head of the default branch
1022 h) tip
1022 h) tip
1023
1023
1024 Returns 0 on success.
1024 Returns 0 on success.
1025 """
1025 """
1026 if opts.get('noupdate') and opts.get('updaterev'):
1026 if opts.get('noupdate') and opts.get('updaterev'):
1027 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1027 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1028
1028
1029 r = hg.clone(hg.remoteui(ui, opts), source, dest,
1029 r = hg.clone(hg.remoteui(ui, opts), source, dest,
1030 pull=opts.get('pull'),
1030 pull=opts.get('pull'),
1031 stream=opts.get('uncompressed'),
1031 stream=opts.get('uncompressed'),
1032 rev=opts.get('rev'),
1032 rev=opts.get('rev'),
1033 update=opts.get('updaterev') or not opts.get('noupdate'),
1033 update=opts.get('updaterev') or not opts.get('noupdate'),
1034 branch=opts.get('branch'))
1034 branch=opts.get('branch'))
1035
1035
1036 return r is None
1036 return r is None
1037
1037
1038 @command('^commit|ci',
1038 @command('^commit|ci',
1039 [('A', 'addremove', None,
1039 [('A', 'addremove', None,
1040 _('mark new/missing files as added/removed before committing')),
1040 _('mark new/missing files as added/removed before committing')),
1041 ('', 'close-branch', None,
1041 ('', 'close-branch', None,
1042 _('mark a branch as closed, hiding it from the branch list')),
1042 _('mark a branch as closed, hiding it from the branch list')),
1043 ] + walkopts + commitopts + commitopts2,
1043 ] + walkopts + commitopts + commitopts2,
1044 _('[OPTION]... [FILE]...'))
1044 _('[OPTION]... [FILE]...'))
1045 def commit(ui, repo, *pats, **opts):
1045 def commit(ui, repo, *pats, **opts):
1046 """commit the specified files or all outstanding changes
1046 """commit the specified files or all outstanding changes
1047
1047
1048 Commit changes to the given files into the repository. Unlike a
1048 Commit changes to the given files into the repository. Unlike a
1049 centralized SCM, this operation is a local operation. See
1049 centralized SCM, this operation is a local operation. See
1050 :hg:`push` for a way to actively distribute your changes.
1050 :hg:`push` for a way to actively distribute your changes.
1051
1051
1052 If a list of files is omitted, all changes reported by :hg:`status`
1052 If a list of files is omitted, all changes reported by :hg:`status`
1053 will be committed.
1053 will be committed.
1054
1054
1055 If you are committing the result of a merge, do not provide any
1055 If you are committing the result of a merge, do not provide any
1056 filenames or -I/-X filters.
1056 filenames or -I/-X filters.
1057
1057
1058 If no commit message is specified, Mercurial starts your
1058 If no commit message is specified, Mercurial starts your
1059 configured editor where you can enter a message. In case your
1059 configured editor where you can enter a message. In case your
1060 commit fails, you will find a backup of your message in
1060 commit fails, you will find a backup of your message in
1061 ``.hg/last-message.txt``.
1061 ``.hg/last-message.txt``.
1062
1062
1063 See :hg:`help dates` for a list of formats valid for -d/--date.
1063 See :hg:`help dates` for a list of formats valid for -d/--date.
1064
1064
1065 Returns 0 on success, 1 if nothing changed.
1065 Returns 0 on success, 1 if nothing changed.
1066 """
1066 """
1067 extra = {}
1067 extra = {}
1068 if opts.get('close_branch'):
1068 if opts.get('close_branch'):
1069 if repo['.'].node() not in repo.branchheads():
1069 if repo['.'].node() not in repo.branchheads():
1070 # The topo heads set is included in the branch heads set of the
1070 # The topo heads set is included in the branch heads set of the
1071 # current branch, so it's sufficient to test branchheads
1071 # current branch, so it's sufficient to test branchheads
1072 raise util.Abort(_('can only close branch heads'))
1072 raise util.Abort(_('can only close branch heads'))
1073 extra['close'] = 1
1073 extra['close'] = 1
1074 e = cmdutil.commiteditor
1074 e = cmdutil.commiteditor
1075 if opts.get('force_editor'):
1075 if opts.get('force_editor'):
1076 e = cmdutil.commitforceeditor
1076 e = cmdutil.commitforceeditor
1077
1077
1078 def commitfunc(ui, repo, message, match, opts):
1078 def commitfunc(ui, repo, message, match, opts):
1079 return repo.commit(message, opts.get('user'), opts.get('date'), match,
1079 return repo.commit(message, opts.get('user'), opts.get('date'), match,
1080 editor=e, extra=extra)
1080 editor=e, extra=extra)
1081
1081
1082 branch = repo[None].branch()
1082 branch = repo[None].branch()
1083 bheads = repo.branchheads(branch)
1083 bheads = repo.branchheads(branch)
1084
1084
1085 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1085 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1086 if not node:
1086 if not node:
1087 stat = repo.status(match=cmdutil.match(repo, pats, opts))
1087 stat = repo.status(match=cmdutil.match(repo, pats, opts))
1088 if stat[3]:
1088 if stat[3]:
1089 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
1089 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
1090 % len(stat[3]))
1090 % len(stat[3]))
1091 else:
1091 else:
1092 ui.status(_("nothing changed\n"))
1092 ui.status(_("nothing changed\n"))
1093 return 1
1093 return 1
1094
1094
1095 ctx = repo[node]
1095 ctx = repo[node]
1096 parents = ctx.parents()
1096 parents = ctx.parents()
1097
1097
1098 if bheads and not [x for x in parents
1098 if bheads and not [x for x in parents
1099 if x.node() in bheads and x.branch() == branch]:
1099 if x.node() in bheads and x.branch() == branch]:
1100 ui.status(_('created new head\n'))
1100 ui.status(_('created new head\n'))
1101 # The message is not printed for initial roots. For the other
1101 # The message is not printed for initial roots. For the other
1102 # changesets, it is printed in the following situations:
1102 # changesets, it is printed in the following situations:
1103 #
1103 #
1104 # Par column: for the 2 parents with ...
1104 # Par column: for the 2 parents with ...
1105 # N: null or no parent
1105 # N: null or no parent
1106 # B: parent is on another named branch
1106 # B: parent is on another named branch
1107 # C: parent is a regular non head changeset
1107 # C: parent is a regular non head changeset
1108 # H: parent was a branch head of the current branch
1108 # H: parent was a branch head of the current branch
1109 # Msg column: whether we print "created new head" message
1109 # Msg column: whether we print "created new head" message
1110 # In the following, it is assumed that there already exists some
1110 # In the following, it is assumed that there already exists some
1111 # initial branch heads of the current branch, otherwise nothing is
1111 # initial branch heads of the current branch, otherwise nothing is
1112 # printed anyway.
1112 # printed anyway.
1113 #
1113 #
1114 # Par Msg Comment
1114 # Par Msg Comment
1115 # NN y additional topo root
1115 # NN y additional topo root
1116 #
1116 #
1117 # BN y additional branch root
1117 # BN y additional branch root
1118 # CN y additional topo head
1118 # CN y additional topo head
1119 # HN n usual case
1119 # HN n usual case
1120 #
1120 #
1121 # BB y weird additional branch root
1121 # BB y weird additional branch root
1122 # CB y branch merge
1122 # CB y branch merge
1123 # HB n merge with named branch
1123 # HB n merge with named branch
1124 #
1124 #
1125 # CC y additional head from merge
1125 # CC y additional head from merge
1126 # CH n merge with a head
1126 # CH n merge with a head
1127 #
1127 #
1128 # HH n head merge: head count decreases
1128 # HH n head merge: head count decreases
1129
1129
1130 if not opts.get('close_branch'):
1130 if not opts.get('close_branch'):
1131 for r in parents:
1131 for r in parents:
1132 if r.extra().get('close') and r.branch() == branch:
1132 if r.extra().get('close') and r.branch() == branch:
1133 ui.status(_('reopening closed branch head %d\n') % r)
1133 ui.status(_('reopening closed branch head %d\n') % r)
1134
1134
1135 if ui.debugflag:
1135 if ui.debugflag:
1136 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1136 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1137 elif ui.verbose:
1137 elif ui.verbose:
1138 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1138 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1139
1139
1140 @command('copy|cp',
1140 @command('copy|cp',
1141 [('A', 'after', None, _('record a copy that has already occurred')),
1141 [('A', 'after', None, _('record a copy that has already occurred')),
1142 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1142 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1143 ] + walkopts + dryrunopts,
1143 ] + walkopts + dryrunopts,
1144 _('[OPTION]... [SOURCE]... DEST'))
1144 _('[OPTION]... [SOURCE]... DEST'))
1145 def copy(ui, repo, *pats, **opts):
1145 def copy(ui, repo, *pats, **opts):
1146 """mark files as copied for the next commit
1146 """mark files as copied for the next commit
1147
1147
1148 Mark dest as having copies of source files. If dest is a
1148 Mark dest as having copies of source files. If dest is a
1149 directory, copies are put in that directory. If dest is a file,
1149 directory, copies are put in that directory. If dest is a file,
1150 the source must be a single file.
1150 the source must be a single file.
1151
1151
1152 By default, this command copies the contents of files as they
1152 By default, this command copies the contents of files as they
1153 exist in the working directory. If invoked with -A/--after, the
1153 exist in the working directory. If invoked with -A/--after, the
1154 operation is recorded, but no copying is performed.
1154 operation is recorded, but no copying is performed.
1155
1155
1156 This command takes effect with the next commit. To undo a copy
1156 This command takes effect with the next commit. To undo a copy
1157 before that, see :hg:`revert`.
1157 before that, see :hg:`revert`.
1158
1158
1159 Returns 0 on success, 1 if errors are encountered.
1159 Returns 0 on success, 1 if errors are encountered.
1160 """
1160 """
1161 wlock = repo.wlock(False)
1161 wlock = repo.wlock(False)
1162 try:
1162 try:
1163 return cmdutil.copy(ui, repo, pats, opts)
1163 return cmdutil.copy(ui, repo, pats, opts)
1164 finally:
1164 finally:
1165 wlock.release()
1165 wlock.release()
1166
1166
1167 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1167 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1168 def debugancestor(ui, repo, *args):
1168 def debugancestor(ui, repo, *args):
1169 """find the ancestor revision of two revisions in a given index"""
1169 """find the ancestor revision of two revisions in a given index"""
1170 if len(args) == 3:
1170 if len(args) == 3:
1171 index, rev1, rev2 = args
1171 index, rev1, rev2 = args
1172 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1172 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1173 lookup = r.lookup
1173 lookup = r.lookup
1174 elif len(args) == 2:
1174 elif len(args) == 2:
1175 if not repo:
1175 if not repo:
1176 raise util.Abort(_("there is no Mercurial repository here "
1176 raise util.Abort(_("there is no Mercurial repository here "
1177 "(.hg not found)"))
1177 "(.hg not found)"))
1178 rev1, rev2 = args
1178 rev1, rev2 = args
1179 r = repo.changelog
1179 r = repo.changelog
1180 lookup = repo.lookup
1180 lookup = repo.lookup
1181 else:
1181 else:
1182 raise util.Abort(_('either two or three arguments required'))
1182 raise util.Abort(_('either two or three arguments required'))
1183 a = r.ancestor(lookup(rev1), lookup(rev2))
1183 a = r.ancestor(lookup(rev1), lookup(rev2))
1184 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1184 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1185
1185
1186 @command('debugbuilddag',
1186 @command('debugbuilddag',
1187 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1187 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1188 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1188 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1189 ('n', 'new-file', None, _('add new file at each rev'))],
1189 ('n', 'new-file', None, _('add new file at each rev'))],
1190 _('[OPTION]... [TEXT]'))
1190 _('[OPTION]... [TEXT]'))
1191 def debugbuilddag(ui, repo, text=None,
1191 def debugbuilddag(ui, repo, text=None,
1192 mergeable_file=False,
1192 mergeable_file=False,
1193 overwritten_file=False,
1193 overwritten_file=False,
1194 new_file=False):
1194 new_file=False):
1195 """builds a repo with a given DAG from scratch in the current empty repo
1195 """builds a repo with a given DAG from scratch in the current empty repo
1196
1196
1197 The description of the DAG is read from stdin if not given on the
1197 The description of the DAG is read from stdin if not given on the
1198 command line.
1198 command line.
1199
1199
1200 Elements:
1200 Elements:
1201
1201
1202 - "+n" is a linear run of n nodes based on the current default parent
1202 - "+n" is a linear run of n nodes based on the current default parent
1203 - "." is a single node based on the current default parent
1203 - "." is a single node based on the current default parent
1204 - "$" resets the default parent to null (implied at the start);
1204 - "$" resets the default parent to null (implied at the start);
1205 otherwise the default parent is always the last node created
1205 otherwise the default parent is always the last node created
1206 - "<p" sets the default parent to the backref p
1206 - "<p" sets the default parent to the backref p
1207 - "*p" is a fork at parent p, which is a backref
1207 - "*p" is a fork at parent p, which is a backref
1208 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1208 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1209 - "/p2" is a merge of the preceding node and p2
1209 - "/p2" is a merge of the preceding node and p2
1210 - ":tag" defines a local tag for the preceding node
1210 - ":tag" defines a local tag for the preceding node
1211 - "@branch" sets the named branch for subsequent nodes
1211 - "@branch" sets the named branch for subsequent nodes
1212 - "#...\\n" is a comment up to the end of the line
1212 - "#...\\n" is a comment up to the end of the line
1213
1213
1214 Whitespace between the above elements is ignored.
1214 Whitespace between the above elements is ignored.
1215
1215
1216 A backref is either
1216 A backref is either
1217
1217
1218 - a number n, which references the node curr-n, where curr is the current
1218 - a number n, which references the node curr-n, where curr is the current
1219 node, or
1219 node, or
1220 - the name of a local tag you placed earlier using ":tag", or
1220 - the name of a local tag you placed earlier using ":tag", or
1221 - empty to denote the default parent.
1221 - empty to denote the default parent.
1222
1222
1223 All string valued-elements are either strictly alphanumeric, or must
1223 All string valued-elements are either strictly alphanumeric, or must
1224 be enclosed in double quotes ("..."), with "\\" as escape character.
1224 be enclosed in double quotes ("..."), with "\\" as escape character.
1225 """
1225 """
1226
1226
1227 if text is None:
1227 if text is None:
1228 ui.status(_("reading DAG from stdin\n"))
1228 ui.status(_("reading DAG from stdin\n"))
1229 text = sys.stdin.read()
1229 text = sys.stdin.read()
1230
1230
1231 cl = repo.changelog
1231 cl = repo.changelog
1232 if len(cl) > 0:
1232 if len(cl) > 0:
1233 raise util.Abort(_('repository is not empty'))
1233 raise util.Abort(_('repository is not empty'))
1234
1234
1235 # determine number of revs in DAG
1235 # determine number of revs in DAG
1236 total = 0
1236 total = 0
1237 for type, data in dagparser.parsedag(text):
1237 for type, data in dagparser.parsedag(text):
1238 if type == 'n':
1238 if type == 'n':
1239 total += 1
1239 total += 1
1240
1240
1241 if mergeable_file:
1241 if mergeable_file:
1242 linesperrev = 2
1242 linesperrev = 2
1243 # make a file with k lines per rev
1243 # make a file with k lines per rev
1244 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1244 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1245 initialmergedlines.append("")
1245 initialmergedlines.append("")
1246
1246
1247 tags = []
1247 tags = []
1248
1248
1249 tr = repo.transaction("builddag")
1249 tr = repo.transaction("builddag")
1250 try:
1250 try:
1251
1251
1252 at = -1
1252 at = -1
1253 atbranch = 'default'
1253 atbranch = 'default'
1254 nodeids = []
1254 nodeids = []
1255 ui.progress(_('building'), 0, unit=_('revisions'), total=total)
1255 ui.progress(_('building'), 0, unit=_('revisions'), total=total)
1256 for type, data in dagparser.parsedag(text):
1256 for type, data in dagparser.parsedag(text):
1257 if type == 'n':
1257 if type == 'n':
1258 ui.note('node %s\n' % str(data))
1258 ui.note('node %s\n' % str(data))
1259 id, ps = data
1259 id, ps = data
1260
1260
1261 files = []
1261 files = []
1262 fctxs = {}
1262 fctxs = {}
1263
1263
1264 p2 = None
1264 p2 = None
1265 if mergeable_file:
1265 if mergeable_file:
1266 fn = "mf"
1266 fn = "mf"
1267 p1 = repo[ps[0]]
1267 p1 = repo[ps[0]]
1268 if len(ps) > 1:
1268 if len(ps) > 1:
1269 p2 = repo[ps[1]]
1269 p2 = repo[ps[1]]
1270 pa = p1.ancestor(p2)
1270 pa = p1.ancestor(p2)
1271 base, local, other = [x[fn].data() for x in pa, p1, p2]
1271 base, local, other = [x[fn].data() for x in pa, p1, p2]
1272 m3 = simplemerge.Merge3Text(base, local, other)
1272 m3 = simplemerge.Merge3Text(base, local, other)
1273 ml = [l.strip() for l in m3.merge_lines()]
1273 ml = [l.strip() for l in m3.merge_lines()]
1274 ml.append("")
1274 ml.append("")
1275 elif at > 0:
1275 elif at > 0:
1276 ml = p1[fn].data().split("\n")
1276 ml = p1[fn].data().split("\n")
1277 else:
1277 else:
1278 ml = initialmergedlines
1278 ml = initialmergedlines
1279 ml[id * linesperrev] += " r%i" % id
1279 ml[id * linesperrev] += " r%i" % id
1280 mergedtext = "\n".join(ml)
1280 mergedtext = "\n".join(ml)
1281 files.append(fn)
1281 files.append(fn)
1282 fctxs[fn] = context.memfilectx(fn, mergedtext)
1282 fctxs[fn] = context.memfilectx(fn, mergedtext)
1283
1283
1284 if overwritten_file:
1284 if overwritten_file:
1285 fn = "of"
1285 fn = "of"
1286 files.append(fn)
1286 files.append(fn)
1287 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1287 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1288
1288
1289 if new_file:
1289 if new_file:
1290 fn = "nf%i" % id
1290 fn = "nf%i" % id
1291 files.append(fn)
1291 files.append(fn)
1292 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1292 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1293 if len(ps) > 1:
1293 if len(ps) > 1:
1294 if not p2:
1294 if not p2:
1295 p2 = repo[ps[1]]
1295 p2 = repo[ps[1]]
1296 for fn in p2:
1296 for fn in p2:
1297 if fn.startswith("nf"):
1297 if fn.startswith("nf"):
1298 files.append(fn)
1298 files.append(fn)
1299 fctxs[fn] = p2[fn]
1299 fctxs[fn] = p2[fn]
1300
1300
1301 def fctxfn(repo, cx, path):
1301 def fctxfn(repo, cx, path):
1302 return fctxs.get(path)
1302 return fctxs.get(path)
1303
1303
1304 if len(ps) == 0 or ps[0] < 0:
1304 if len(ps) == 0 or ps[0] < 0:
1305 pars = [None, None]
1305 pars = [None, None]
1306 elif len(ps) == 1:
1306 elif len(ps) == 1:
1307 pars = [nodeids[ps[0]], None]
1307 pars = [nodeids[ps[0]], None]
1308 else:
1308 else:
1309 pars = [nodeids[p] for p in ps]
1309 pars = [nodeids[p] for p in ps]
1310 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1310 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1311 date=(id, 0),
1311 date=(id, 0),
1312 user="debugbuilddag",
1312 user="debugbuilddag",
1313 extra={'branch': atbranch})
1313 extra={'branch': atbranch})
1314 nodeid = repo.commitctx(cx)
1314 nodeid = repo.commitctx(cx)
1315 nodeids.append(nodeid)
1315 nodeids.append(nodeid)
1316 at = id
1316 at = id
1317 elif type == 'l':
1317 elif type == 'l':
1318 id, name = data
1318 id, name = data
1319 ui.note('tag %s\n' % name)
1319 ui.note('tag %s\n' % name)
1320 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1320 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1321 elif type == 'a':
1321 elif type == 'a':
1322 ui.note('branch %s\n' % data)
1322 ui.note('branch %s\n' % data)
1323 atbranch = data
1323 atbranch = data
1324 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1324 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1325 tr.close()
1325 tr.close()
1326 finally:
1326 finally:
1327 ui.progress(_('building'), None)
1327 ui.progress(_('building'), None)
1328 tr.release()
1328 tr.release()
1329
1329
1330 if tags:
1330 if tags:
1331 repo.opener.write("localtags", "".join(tags))
1331 repo.opener.write("localtags", "".join(tags))
1332
1332
1333 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1333 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1334 def debugbundle(ui, bundlepath, all=None, **opts):
1334 def debugbundle(ui, bundlepath, all=None, **opts):
1335 """lists the contents of a bundle"""
1335 """lists the contents of a bundle"""
1336 f = url.open(ui, bundlepath)
1336 f = url.open(ui, bundlepath)
1337 try:
1337 try:
1338 gen = changegroup.readbundle(f, bundlepath)
1338 gen = changegroup.readbundle(f, bundlepath)
1339 if all:
1339 if all:
1340 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1340 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1341
1341
1342 def showchunks(named):
1342 def showchunks(named):
1343 ui.write("\n%s\n" % named)
1343 ui.write("\n%s\n" % named)
1344 chain = None
1344 chain = None
1345 while 1:
1345 while 1:
1346 chunkdata = gen.deltachunk(chain)
1346 chunkdata = gen.deltachunk(chain)
1347 if not chunkdata:
1347 if not chunkdata:
1348 break
1348 break
1349 node = chunkdata['node']
1349 node = chunkdata['node']
1350 p1 = chunkdata['p1']
1350 p1 = chunkdata['p1']
1351 p2 = chunkdata['p2']
1351 p2 = chunkdata['p2']
1352 cs = chunkdata['cs']
1352 cs = chunkdata['cs']
1353 deltabase = chunkdata['deltabase']
1353 deltabase = chunkdata['deltabase']
1354 delta = chunkdata['delta']
1354 delta = chunkdata['delta']
1355 ui.write("%s %s %s %s %s %s\n" %
1355 ui.write("%s %s %s %s %s %s\n" %
1356 (hex(node), hex(p1), hex(p2),
1356 (hex(node), hex(p1), hex(p2),
1357 hex(cs), hex(deltabase), len(delta)))
1357 hex(cs), hex(deltabase), len(delta)))
1358 chain = node
1358 chain = node
1359
1359
1360 chunkdata = gen.changelogheader()
1360 chunkdata = gen.changelogheader()
1361 showchunks("changelog")
1361 showchunks("changelog")
1362 chunkdata = gen.manifestheader()
1362 chunkdata = gen.manifestheader()
1363 showchunks("manifest")
1363 showchunks("manifest")
1364 while 1:
1364 while 1:
1365 chunkdata = gen.filelogheader()
1365 chunkdata = gen.filelogheader()
1366 if not chunkdata:
1366 if not chunkdata:
1367 break
1367 break
1368 fname = chunkdata['filename']
1368 fname = chunkdata['filename']
1369 showchunks(fname)
1369 showchunks(fname)
1370 else:
1370 else:
1371 chunkdata = gen.changelogheader()
1371 chunkdata = gen.changelogheader()
1372 chain = None
1372 chain = None
1373 while 1:
1373 while 1:
1374 chunkdata = gen.deltachunk(chain)
1374 chunkdata = gen.deltachunk(chain)
1375 if not chunkdata:
1375 if not chunkdata:
1376 break
1376 break
1377 node = chunkdata['node']
1377 node = chunkdata['node']
1378 ui.write("%s\n" % hex(node))
1378 ui.write("%s\n" % hex(node))
1379 chain = node
1379 chain = node
1380 finally:
1380 finally:
1381 f.close()
1381 f.close()
1382
1382
1383 @command('debugcheckstate', [], '')
1383 @command('debugcheckstate', [], '')
1384 def debugcheckstate(ui, repo):
1384 def debugcheckstate(ui, repo):
1385 """validate the correctness of the current dirstate"""
1385 """validate the correctness of the current dirstate"""
1386 parent1, parent2 = repo.dirstate.parents()
1386 parent1, parent2 = repo.dirstate.parents()
1387 m1 = repo[parent1].manifest()
1387 m1 = repo[parent1].manifest()
1388 m2 = repo[parent2].manifest()
1388 m2 = repo[parent2].manifest()
1389 errors = 0
1389 errors = 0
1390 for f in repo.dirstate:
1390 for f in repo.dirstate:
1391 state = repo.dirstate[f]
1391 state = repo.dirstate[f]
1392 if state in "nr" and f not in m1:
1392 if state in "nr" and f not in m1:
1393 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1393 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1394 errors += 1
1394 errors += 1
1395 if state in "a" and f in m1:
1395 if state in "a" and f in m1:
1396 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1396 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1397 errors += 1
1397 errors += 1
1398 if state in "m" and f not in m1 and f not in m2:
1398 if state in "m" and f not in m1 and f not in m2:
1399 ui.warn(_("%s in state %s, but not in either manifest\n") %
1399 ui.warn(_("%s in state %s, but not in either manifest\n") %
1400 (f, state))
1400 (f, state))
1401 errors += 1
1401 errors += 1
1402 for f in m1:
1402 for f in m1:
1403 state = repo.dirstate[f]
1403 state = repo.dirstate[f]
1404 if state not in "nrm":
1404 if state not in "nrm":
1405 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1405 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1406 errors += 1
1406 errors += 1
1407 if errors:
1407 if errors:
1408 error = _(".hg/dirstate inconsistent with current parent's manifest")
1408 error = _(".hg/dirstate inconsistent with current parent's manifest")
1409 raise util.Abort(error)
1409 raise util.Abort(error)
1410
1410
1411 @command('debugcommands', [], _('[COMMAND]'))
1411 @command('debugcommands', [], _('[COMMAND]'))
1412 def debugcommands(ui, cmd='', *args):
1412 def debugcommands(ui, cmd='', *args):
1413 """list all available commands and options"""
1413 """list all available commands and options"""
1414 for cmd, vals in sorted(table.iteritems()):
1414 for cmd, vals in sorted(table.iteritems()):
1415 cmd = cmd.split('|')[0].strip('^')
1415 cmd = cmd.split('|')[0].strip('^')
1416 opts = ', '.join([i[1] for i in vals[1]])
1416 opts = ', '.join([i[1] for i in vals[1]])
1417 ui.write('%s: %s\n' % (cmd, opts))
1417 ui.write('%s: %s\n' % (cmd, opts))
1418
1418
1419 @command('debugcomplete',
1419 @command('debugcomplete',
1420 [('o', 'options', None, _('show the command options'))],
1420 [('o', 'options', None, _('show the command options'))],
1421 _('[-o] CMD'))
1421 _('[-o] CMD'))
1422 def debugcomplete(ui, cmd='', **opts):
1422 def debugcomplete(ui, cmd='', **opts):
1423 """returns the completion list associated with the given command"""
1423 """returns the completion list associated with the given command"""
1424
1424
1425 if opts.get('options'):
1425 if opts.get('options'):
1426 options = []
1426 options = []
1427 otables = [globalopts]
1427 otables = [globalopts]
1428 if cmd:
1428 if cmd:
1429 aliases, entry = cmdutil.findcmd(cmd, table, False)
1429 aliases, entry = cmdutil.findcmd(cmd, table, False)
1430 otables.append(entry[1])
1430 otables.append(entry[1])
1431 for t in otables:
1431 for t in otables:
1432 for o in t:
1432 for o in t:
1433 if "(DEPRECATED)" in o[3]:
1433 if "(DEPRECATED)" in o[3]:
1434 continue
1434 continue
1435 if o[0]:
1435 if o[0]:
1436 options.append('-%s' % o[0])
1436 options.append('-%s' % o[0])
1437 options.append('--%s' % o[1])
1437 options.append('--%s' % o[1])
1438 ui.write("%s\n" % "\n".join(options))
1438 ui.write("%s\n" % "\n".join(options))
1439 return
1439 return
1440
1440
1441 cmdlist = cmdutil.findpossible(cmd, table)
1441 cmdlist = cmdutil.findpossible(cmd, table)
1442 if ui.verbose:
1442 if ui.verbose:
1443 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1443 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1444 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1444 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1445
1445
1446 @command('debugdag',
1446 @command('debugdag',
1447 [('t', 'tags', None, _('use tags as labels')),
1447 [('t', 'tags', None, _('use tags as labels')),
1448 ('b', 'branches', None, _('annotate with branch names')),
1448 ('b', 'branches', None, _('annotate with branch names')),
1449 ('', 'dots', None, _('use dots for runs')),
1449 ('', 'dots', None, _('use dots for runs')),
1450 ('s', 'spaces', None, _('separate elements by spaces'))],
1450 ('s', 'spaces', None, _('separate elements by spaces'))],
1451 _('[OPTION]... [FILE [REV]...]'))
1451 _('[OPTION]... [FILE [REV]...]'))
1452 def debugdag(ui, repo, file_=None, *revs, **opts):
1452 def debugdag(ui, repo, file_=None, *revs, **opts):
1453 """format the changelog or an index DAG as a concise textual description
1453 """format the changelog or an index DAG as a concise textual description
1454
1454
1455 If you pass a revlog index, the revlog's DAG is emitted. If you list
1455 If you pass a revlog index, the revlog's DAG is emitted. If you list
1456 revision numbers, they get labelled in the output as rN.
1456 revision numbers, they get labelled in the output as rN.
1457
1457
1458 Otherwise, the changelog DAG of the current repo is emitted.
1458 Otherwise, the changelog DAG of the current repo is emitted.
1459 """
1459 """
1460 spaces = opts.get('spaces')
1460 spaces = opts.get('spaces')
1461 dots = opts.get('dots')
1461 dots = opts.get('dots')
1462 if file_:
1462 if file_:
1463 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1463 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1464 revs = set((int(r) for r in revs))
1464 revs = set((int(r) for r in revs))
1465 def events():
1465 def events():
1466 for r in rlog:
1466 for r in rlog:
1467 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1467 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1468 if r in revs:
1468 if r in revs:
1469 yield 'l', (r, "r%i" % r)
1469 yield 'l', (r, "r%i" % r)
1470 elif repo:
1470 elif repo:
1471 cl = repo.changelog
1471 cl = repo.changelog
1472 tags = opts.get('tags')
1472 tags = opts.get('tags')
1473 branches = opts.get('branches')
1473 branches = opts.get('branches')
1474 if tags:
1474 if tags:
1475 labels = {}
1475 labels = {}
1476 for l, n in repo.tags().items():
1476 for l, n in repo.tags().items():
1477 labels.setdefault(cl.rev(n), []).append(l)
1477 labels.setdefault(cl.rev(n), []).append(l)
1478 def events():
1478 def events():
1479 b = "default"
1479 b = "default"
1480 for r in cl:
1480 for r in cl:
1481 if branches:
1481 if branches:
1482 newb = cl.read(cl.node(r))[5]['branch']
1482 newb = cl.read(cl.node(r))[5]['branch']
1483 if newb != b:
1483 if newb != b:
1484 yield 'a', newb
1484 yield 'a', newb
1485 b = newb
1485 b = newb
1486 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1486 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1487 if tags:
1487 if tags:
1488 ls = labels.get(r)
1488 ls = labels.get(r)
1489 if ls:
1489 if ls:
1490 for l in ls:
1490 for l in ls:
1491 yield 'l', (r, l)
1491 yield 'l', (r, l)
1492 else:
1492 else:
1493 raise util.Abort(_('need repo for changelog dag'))
1493 raise util.Abort(_('need repo for changelog dag'))
1494
1494
1495 for line in dagparser.dagtextlines(events(),
1495 for line in dagparser.dagtextlines(events(),
1496 addspaces=spaces,
1496 addspaces=spaces,
1497 wraplabels=True,
1497 wraplabels=True,
1498 wrapannotations=True,
1498 wrapannotations=True,
1499 wrapnonlinear=dots,
1499 wrapnonlinear=dots,
1500 usedots=dots,
1500 usedots=dots,
1501 maxlinewidth=70):
1501 maxlinewidth=70):
1502 ui.write(line)
1502 ui.write(line)
1503 ui.write("\n")
1503 ui.write("\n")
1504
1504
1505 @command('debugdata', [], _('FILE REV'))
1505 @command('debugdata', [], _('FILE REV'))
1506 def debugdata(ui, repo, file_, rev):
1506 def debugdata(ui, repo, file_, rev):
1507 """dump the contents of a data file revision"""
1507 """dump the contents of a data file revision"""
1508 r = None
1508 r = None
1509 if repo:
1509 if repo:
1510 filelog = repo.file(file_)
1510 filelog = repo.file(file_)
1511 if len(filelog):
1511 if len(filelog):
1512 r = filelog
1512 r = filelog
1513 if not r:
1513 if not r:
1514 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
1514 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False),
1515 file_[:-2] + ".i")
1515 file_[:-2] + ".i")
1516 try:
1516 try:
1517 ui.write(r.revision(r.lookup(rev)))
1517 ui.write(r.revision(r.lookup(rev)))
1518 except KeyError:
1518 except KeyError:
1519 raise util.Abort(_('invalid revision identifier %s') % rev)
1519 raise util.Abort(_('invalid revision identifier %s') % rev)
1520
1520
1521 @command('debugdate',
1521 @command('debugdate',
1522 [('e', 'extended', None, _('try extended date formats'))],
1522 [('e', 'extended', None, _('try extended date formats'))],
1523 _('[-e] DATE [RANGE]'))
1523 _('[-e] DATE [RANGE]'))
1524 def debugdate(ui, date, range=None, **opts):
1524 def debugdate(ui, date, range=None, **opts):
1525 """parse and display a date"""
1525 """parse and display a date"""
1526 if opts["extended"]:
1526 if opts["extended"]:
1527 d = util.parsedate(date, util.extendeddateformats)
1527 d = util.parsedate(date, util.extendeddateformats)
1528 else:
1528 else:
1529 d = util.parsedate(date)
1529 d = util.parsedate(date)
1530 ui.write("internal: %s %s\n" % d)
1530 ui.write("internal: %s %s\n" % d)
1531 ui.write("standard: %s\n" % util.datestr(d))
1531 ui.write("standard: %s\n" % util.datestr(d))
1532 if range:
1532 if range:
1533 m = util.matchdate(range)
1533 m = util.matchdate(range)
1534 ui.write("match: %s\n" % m(d[0]))
1534 ui.write("match: %s\n" % m(d[0]))
1535
1535
1536 @command('debugdiscovery',
1536 @command('debugdiscovery',
1537 [('', 'old', None, _('use old-style discovery')),
1537 [('', 'old', None, _('use old-style discovery')),
1538 ('', 'nonheads', None,
1538 ('', 'nonheads', None,
1539 _('use old-style discovery with non-heads included')),
1539 _('use old-style discovery with non-heads included')),
1540 ] + remoteopts,
1540 ] + remoteopts,
1541 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1541 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1542 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1542 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1543 """runs the changeset discovery protocol in isolation"""
1543 """runs the changeset discovery protocol in isolation"""
1544 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1544 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1545 remote = hg.repository(hg.remoteui(repo, opts), remoteurl)
1545 remote = hg.repository(hg.remoteui(repo, opts), remoteurl)
1546 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1546 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1547
1547
1548 # make sure tests are repeatable
1548 # make sure tests are repeatable
1549 random.seed(12323)
1549 random.seed(12323)
1550
1550
1551 def doit(localheads, remoteheads):
1551 def doit(localheads, remoteheads):
1552 if opts.get('old'):
1552 if opts.get('old'):
1553 if localheads:
1553 if localheads:
1554 raise util.Abort('cannot use localheads with old style discovery')
1554 raise util.Abort('cannot use localheads with old style discovery')
1555 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1555 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1556 force=True)
1556 force=True)
1557 common = set(common)
1557 common = set(common)
1558 if not opts.get('nonheads'):
1558 if not opts.get('nonheads'):
1559 ui.write("unpruned common: %s\n" % " ".join([short(n)
1559 ui.write("unpruned common: %s\n" % " ".join([short(n)
1560 for n in common]))
1560 for n in common]))
1561 dag = dagutil.revlogdag(repo.changelog)
1561 dag = dagutil.revlogdag(repo.changelog)
1562 all = dag.ancestorset(dag.internalizeall(common))
1562 all = dag.ancestorset(dag.internalizeall(common))
1563 common = dag.externalizeall(dag.headsetofconnecteds(all))
1563 common = dag.externalizeall(dag.headsetofconnecteds(all))
1564 else:
1564 else:
1565 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1565 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1566 common = set(common)
1566 common = set(common)
1567 rheads = set(hds)
1567 rheads = set(hds)
1568 lheads = set(repo.heads())
1568 lheads = set(repo.heads())
1569 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1569 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1570 if lheads <= common:
1570 if lheads <= common:
1571 ui.write("local is subset\n")
1571 ui.write("local is subset\n")
1572 elif rheads <= common:
1572 elif rheads <= common:
1573 ui.write("remote is subset\n")
1573 ui.write("remote is subset\n")
1574
1574
1575 serverlogs = opts.get('serverlog')
1575 serverlogs = opts.get('serverlog')
1576 if serverlogs:
1576 if serverlogs:
1577 for filename in serverlogs:
1577 for filename in serverlogs:
1578 logfile = open(filename, 'r')
1578 logfile = open(filename, 'r')
1579 try:
1579 try:
1580 line = logfile.readline()
1580 line = logfile.readline()
1581 while line:
1581 while line:
1582 parts = line.strip().split(';')
1582 parts = line.strip().split(';')
1583 op = parts[1]
1583 op = parts[1]
1584 if op == 'cg':
1584 if op == 'cg':
1585 pass
1585 pass
1586 elif op == 'cgss':
1586 elif op == 'cgss':
1587 doit(parts[2].split(' '), parts[3].split(' '))
1587 doit(parts[2].split(' '), parts[3].split(' '))
1588 elif op == 'unb':
1588 elif op == 'unb':
1589 doit(parts[3].split(' '), parts[2].split(' '))
1589 doit(parts[3].split(' '), parts[2].split(' '))
1590 line = logfile.readline()
1590 line = logfile.readline()
1591 finally:
1591 finally:
1592 logfile.close()
1592 logfile.close()
1593
1593
1594 else:
1594 else:
1595 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1595 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1596 opts.get('remote_head'))
1596 opts.get('remote_head'))
1597 localrevs = opts.get('local_head')
1597 localrevs = opts.get('local_head')
1598 doit(localrevs, remoterevs)
1598 doit(localrevs, remoterevs)
1599
1599
1600 @command('debugfsinfo', [], _('[PATH]'))
1600 @command('debugfsinfo', [], _('[PATH]'))
1601 def debugfsinfo(ui, path = "."):
1601 def debugfsinfo(ui, path = "."):
1602 """show information detected about current filesystem"""
1602 """show information detected about current filesystem"""
1603 util.writefile('.debugfsinfo', '')
1603 util.writefile('.debugfsinfo', '')
1604 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1604 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1605 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1605 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1606 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1606 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1607 and 'yes' or 'no'))
1607 and 'yes' or 'no'))
1608 os.unlink('.debugfsinfo')
1608 os.unlink('.debugfsinfo')
1609
1609
1610 @command('debuggetbundle',
1610 @command('debuggetbundle',
1611 [('H', 'head', [], _('id of head node'), _('ID')),
1611 [('H', 'head', [], _('id of head node'), _('ID')),
1612 ('C', 'common', [], _('id of common node'), _('ID')),
1612 ('C', 'common', [], _('id of common node'), _('ID')),
1613 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1613 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1614 _('REPO FILE [-H|-C ID]...'))
1614 _('REPO FILE [-H|-C ID]...'))
1615 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1615 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1616 """retrieves a bundle from a repo
1616 """retrieves a bundle from a repo
1617
1617
1618 Every ID must be a full-length hex node id string. Saves the bundle to the
1618 Every ID must be a full-length hex node id string. Saves the bundle to the
1619 given file.
1619 given file.
1620 """
1620 """
1621 repo = hg.repository(ui, repopath)
1621 repo = hg.repository(ui, repopath)
1622 if not repo.capable('getbundle'):
1622 if not repo.capable('getbundle'):
1623 raise util.Abort("getbundle() not supported by target repository")
1623 raise util.Abort("getbundle() not supported by target repository")
1624 args = {}
1624 args = {}
1625 if common:
1625 if common:
1626 args['common'] = [bin(s) for s in common]
1626 args['common'] = [bin(s) for s in common]
1627 if head:
1627 if head:
1628 args['heads'] = [bin(s) for s in head]
1628 args['heads'] = [bin(s) for s in head]
1629 bundle = repo.getbundle('debug', **args)
1629 bundle = repo.getbundle('debug', **args)
1630
1630
1631 bundletype = opts.get('type', 'bzip2').lower()
1631 bundletype = opts.get('type', 'bzip2').lower()
1632 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1632 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1633 bundletype = btypes.get(bundletype)
1633 bundletype = btypes.get(bundletype)
1634 if bundletype not in changegroup.bundletypes:
1634 if bundletype not in changegroup.bundletypes:
1635 raise util.Abort(_('unknown bundle type specified with --type'))
1635 raise util.Abort(_('unknown bundle type specified with --type'))
1636 changegroup.writebundle(bundle, bundlepath, bundletype)
1636 changegroup.writebundle(bundle, bundlepath, bundletype)
1637
1637
1638 @command('debugignore', [], '')
1638 @command('debugignore', [], '')
1639 def debugignore(ui, repo, *values, **opts):
1639 def debugignore(ui, repo, *values, **opts):
1640 """display the combined ignore pattern"""
1640 """display the combined ignore pattern"""
1641 ignore = repo.dirstate._ignore
1641 ignore = repo.dirstate._ignore
1642 if hasattr(ignore, 'includepat'):
1642 if hasattr(ignore, 'includepat'):
1643 ui.write("%s\n" % ignore.includepat)
1643 ui.write("%s\n" % ignore.includepat)
1644 else:
1644 else:
1645 raise util.Abort(_("no ignore patterns found"))
1645 raise util.Abort(_("no ignore patterns found"))
1646
1646
1647 @command('debugindex',
1647 @command('debugindex',
1648 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1648 [('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1649 _('FILE'))
1649 _('FILE'))
1650 def debugindex(ui, repo, file_, **opts):
1650 def debugindex(ui, repo, file_, **opts):
1651 """dump the contents of an index file"""
1651 """dump the contents of an index file"""
1652 r = None
1652 r = None
1653 if repo:
1653 if repo:
1654 filelog = repo.file(file_)
1654 filelog = repo.file(file_)
1655 if len(filelog):
1655 if len(filelog):
1656 r = filelog
1656 r = filelog
1657
1657
1658 format = opts.get('format', 0)
1658 format = opts.get('format', 0)
1659 if format not in (0, 1):
1659 if format not in (0, 1):
1660 raise util.Abort(_("unknown format %d") % format)
1660 raise util.Abort(_("unknown format %d") % format)
1661
1661
1662 if not r:
1662 if not r:
1663 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1663 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1664
1664
1665 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1665 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1666 if generaldelta:
1666 if generaldelta:
1667 basehdr = ' delta'
1667 basehdr = ' delta'
1668 else:
1668 else:
1669 basehdr = ' base'
1669 basehdr = ' base'
1670
1670
1671 if format == 0:
1671 if format == 0:
1672 ui.write(" rev offset length " + basehdr + " linkrev"
1672 ui.write(" rev offset length " + basehdr + " linkrev"
1673 " nodeid p1 p2\n")
1673 " nodeid p1 p2\n")
1674 elif format == 1:
1674 elif format == 1:
1675 ui.write(" rev flag offset length"
1675 ui.write(" rev flag offset length"
1676 " size " + basehdr + " link p1 p2 nodeid\n")
1676 " size " + basehdr + " link p1 p2 nodeid\n")
1677
1677
1678 for i in r:
1678 for i in r:
1679 node = r.node(i)
1679 node = r.node(i)
1680 if generaldelta:
1680 if generaldelta:
1681 base = r.deltaparent(i)
1681 base = r.deltaparent(i)
1682 else:
1682 else:
1683 base = r.chainbase(i)
1683 base = r.chainbase(i)
1684 if format == 0:
1684 if format == 0:
1685 try:
1685 try:
1686 pp = r.parents(node)
1686 pp = r.parents(node)
1687 except:
1687 except:
1688 pp = [nullid, nullid]
1688 pp = [nullid, nullid]
1689 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1689 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1690 i, r.start(i), r.length(i), base, r.linkrev(i),
1690 i, r.start(i), r.length(i), base, r.linkrev(i),
1691 short(node), short(pp[0]), short(pp[1])))
1691 short(node), short(pp[0]), short(pp[1])))
1692 elif format == 1:
1692 elif format == 1:
1693 pr = r.parentrevs(i)
1693 pr = r.parentrevs(i)
1694 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1694 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1695 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1695 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1696 base, r.linkrev(i), pr[0], pr[1], short(node)))
1696 base, r.linkrev(i), pr[0], pr[1], short(node)))
1697
1697
1698 @command('debugindexdot', [], _('FILE'))
1698 @command('debugindexdot', [], _('FILE'))
1699 def debugindexdot(ui, repo, file_):
1699 def debugindexdot(ui, repo, file_):
1700 """dump an index DAG as a graphviz dot file"""
1700 """dump an index DAG as a graphviz dot file"""
1701 r = None
1701 r = None
1702 if repo:
1702 if repo:
1703 filelog = repo.file(file_)
1703 filelog = repo.file(file_)
1704 if len(filelog):
1704 if len(filelog):
1705 r = filelog
1705 r = filelog
1706 if not r:
1706 if not r:
1707 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1707 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1708 ui.write("digraph G {\n")
1708 ui.write("digraph G {\n")
1709 for i in r:
1709 for i in r:
1710 node = r.node(i)
1710 node = r.node(i)
1711 pp = r.parents(node)
1711 pp = r.parents(node)
1712 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1712 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1713 if pp[1] != nullid:
1713 if pp[1] != nullid:
1714 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1714 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1715 ui.write("}\n")
1715 ui.write("}\n")
1716
1716
1717 @command('debuginstall', [], '')
1717 @command('debuginstall', [], '')
1718 def debuginstall(ui):
1718 def debuginstall(ui):
1719 '''test Mercurial installation
1719 '''test Mercurial installation
1720
1720
1721 Returns 0 on success.
1721 Returns 0 on success.
1722 '''
1722 '''
1723
1723
1724 def writetemp(contents):
1724 def writetemp(contents):
1725 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1725 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1726 f = os.fdopen(fd, "wb")
1726 f = os.fdopen(fd, "wb")
1727 f.write(contents)
1727 f.write(contents)
1728 f.close()
1728 f.close()
1729 return name
1729 return name
1730
1730
1731 problems = 0
1731 problems = 0
1732
1732
1733 # encoding
1733 # encoding
1734 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1734 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1735 try:
1735 try:
1736 encoding.fromlocal("test")
1736 encoding.fromlocal("test")
1737 except util.Abort, inst:
1737 except util.Abort, inst:
1738 ui.write(" %s\n" % inst)
1738 ui.write(" %s\n" % inst)
1739 ui.write(_(" (check that your locale is properly set)\n"))
1739 ui.write(_(" (check that your locale is properly set)\n"))
1740 problems += 1
1740 problems += 1
1741
1741
1742 # compiled modules
1742 # compiled modules
1743 ui.status(_("Checking installed modules (%s)...\n")
1743 ui.status(_("Checking installed modules (%s)...\n")
1744 % os.path.dirname(__file__))
1744 % os.path.dirname(__file__))
1745 try:
1745 try:
1746 import bdiff, mpatch, base85, osutil
1746 import bdiff, mpatch, base85, osutil
1747 except Exception, inst:
1747 except Exception, inst:
1748 ui.write(" %s\n" % inst)
1748 ui.write(" %s\n" % inst)
1749 ui.write(_(" One or more extensions could not be found"))
1749 ui.write(_(" One or more extensions could not be found"))
1750 ui.write(_(" (check that you compiled the extensions)\n"))
1750 ui.write(_(" (check that you compiled the extensions)\n"))
1751 problems += 1
1751 problems += 1
1752
1752
1753 # templates
1753 # templates
1754 ui.status(_("Checking templates...\n"))
1754 ui.status(_("Checking templates...\n"))
1755 try:
1755 try:
1756 import templater
1756 import templater
1757 templater.templater(templater.templatepath("map-cmdline.default"))
1757 templater.templater(templater.templatepath("map-cmdline.default"))
1758 except Exception, inst:
1758 except Exception, inst:
1759 ui.write(" %s\n" % inst)
1759 ui.write(" %s\n" % inst)
1760 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1760 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1761 problems += 1
1761 problems += 1
1762
1762
1763 # editor
1763 # editor
1764 ui.status(_("Checking commit editor...\n"))
1764 ui.status(_("Checking commit editor...\n"))
1765 editor = ui.geteditor()
1765 editor = ui.geteditor()
1766 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1766 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1767 if not cmdpath:
1767 if not cmdpath:
1768 if editor == 'vi':
1768 if editor == 'vi':
1769 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1769 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1770 ui.write(_(" (specify a commit editor in your configuration"
1770 ui.write(_(" (specify a commit editor in your configuration"
1771 " file)\n"))
1771 " file)\n"))
1772 else:
1772 else:
1773 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1773 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1774 ui.write(_(" (specify a commit editor in your configuration"
1774 ui.write(_(" (specify a commit editor in your configuration"
1775 " file)\n"))
1775 " file)\n"))
1776 problems += 1
1776 problems += 1
1777
1777
1778 # check username
1778 # check username
1779 ui.status(_("Checking username...\n"))
1779 ui.status(_("Checking username...\n"))
1780 try:
1780 try:
1781 ui.username()
1781 ui.username()
1782 except util.Abort, e:
1782 except util.Abort, e:
1783 ui.write(" %s\n" % e)
1783 ui.write(" %s\n" % e)
1784 ui.write(_(" (specify a username in your configuration file)\n"))
1784 ui.write(_(" (specify a username in your configuration file)\n"))
1785 problems += 1
1785 problems += 1
1786
1786
1787 if not problems:
1787 if not problems:
1788 ui.status(_("No problems detected\n"))
1788 ui.status(_("No problems detected\n"))
1789 else:
1789 else:
1790 ui.write(_("%s problems detected,"
1790 ui.write(_("%s problems detected,"
1791 " please check your install!\n") % problems)
1791 " please check your install!\n") % problems)
1792
1792
1793 return problems
1793 return problems
1794
1794
1795 @command('debugknown', [], _('REPO ID...'))
1795 @command('debugknown', [], _('REPO ID...'))
1796 def debugknown(ui, repopath, *ids, **opts):
1796 def debugknown(ui, repopath, *ids, **opts):
1797 """test whether node ids are known to a repo
1797 """test whether node ids are known to a repo
1798
1798
1799 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1799 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1800 indicating unknown/known.
1800 indicating unknown/known.
1801 """
1801 """
1802 repo = hg.repository(ui, repopath)
1802 repo = hg.repository(ui, repopath)
1803 if not repo.capable('known'):
1803 if not repo.capable('known'):
1804 raise util.Abort("known() not supported by target repository")
1804 raise util.Abort("known() not supported by target repository")
1805 flags = repo.known([bin(s) for s in ids])
1805 flags = repo.known([bin(s) for s in ids])
1806 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1806 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1807
1807
1808 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
1808 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
1809 def debugpushkey(ui, repopath, namespace, *keyinfo):
1809 def debugpushkey(ui, repopath, namespace, *keyinfo):
1810 '''access the pushkey key/value protocol
1810 '''access the pushkey key/value protocol
1811
1811
1812 With two args, list the keys in the given namespace.
1812 With two args, list the keys in the given namespace.
1813
1813
1814 With five args, set a key to new if it currently is set to old.
1814 With five args, set a key to new if it currently is set to old.
1815 Reports success or failure.
1815 Reports success or failure.
1816 '''
1816 '''
1817
1817
1818 target = hg.repository(ui, repopath)
1818 target = hg.repository(ui, repopath)
1819 if keyinfo:
1819 if keyinfo:
1820 key, old, new = keyinfo
1820 key, old, new = keyinfo
1821 r = target.pushkey(namespace, key, old, new)
1821 r = target.pushkey(namespace, key, old, new)
1822 ui.status(str(r) + '\n')
1822 ui.status(str(r) + '\n')
1823 return not r
1823 return not r
1824 else:
1824 else:
1825 for k, v in target.listkeys(namespace).iteritems():
1825 for k, v in target.listkeys(namespace).iteritems():
1826 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1826 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1827 v.encode('string-escape')))
1827 v.encode('string-escape')))
1828
1828
1829 @command('debugrebuildstate',
1829 @command('debugrebuildstate',
1830 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
1830 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
1831 _('[-r REV] [REV]'))
1831 _('[-r REV] [REV]'))
1832 def debugrebuildstate(ui, repo, rev="tip"):
1832 def debugrebuildstate(ui, repo, rev="tip"):
1833 """rebuild the dirstate as it would look like for the given revision"""
1833 """rebuild the dirstate as it would look like for the given revision"""
1834 ctx = cmdutil.revsingle(repo, rev)
1834 ctx = cmdutil.revsingle(repo, rev)
1835 wlock = repo.wlock()
1835 wlock = repo.wlock()
1836 try:
1836 try:
1837 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1837 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1838 finally:
1838 finally:
1839 wlock.release()
1839 wlock.release()
1840
1840
1841 @command('debugrename',
1841 @command('debugrename',
1842 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1842 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1843 _('[-r REV] FILE'))
1843 _('[-r REV] FILE'))
1844 def debugrename(ui, repo, file1, *pats, **opts):
1844 def debugrename(ui, repo, file1, *pats, **opts):
1845 """dump rename information"""
1845 """dump rename information"""
1846
1846
1847 ctx = cmdutil.revsingle(repo, opts.get('rev'))
1847 ctx = cmdutil.revsingle(repo, opts.get('rev'))
1848 m = cmdutil.match(repo, (file1,) + pats, opts)
1848 m = cmdutil.match(repo, (file1,) + pats, opts)
1849 for abs in ctx.walk(m):
1849 for abs in ctx.walk(m):
1850 fctx = ctx[abs]
1850 fctx = ctx[abs]
1851 o = fctx.filelog().renamed(fctx.filenode())
1851 o = fctx.filelog().renamed(fctx.filenode())
1852 rel = m.rel(abs)
1852 rel = m.rel(abs)
1853 if o:
1853 if o:
1854 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1854 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1855 else:
1855 else:
1856 ui.write(_("%s not renamed\n") % rel)
1856 ui.write(_("%s not renamed\n") % rel)
1857
1857
1858 @command('debugrevlog', [], _('FILE'))
1858 @command('debugrevlog', [], _('FILE'))
1859 def debugrevlog(ui, repo, file_):
1859 def debugrevlog(ui, repo, file_):
1860 """show data and statistics about a revlog"""
1860 """show data and statistics about a revlog"""
1861 r = None
1861 r = None
1862 if repo:
1862 if repo:
1863 filelog = repo.file(file_)
1863 filelog = repo.file(file_)
1864 if len(filelog):
1864 if len(filelog):
1865 r = filelog
1865 r = filelog
1866 if not r:
1866 if not r:
1867 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1867 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1868
1868
1869 v = r.version
1869 v = r.version
1870 format = v & 0xFFFF
1870 format = v & 0xFFFF
1871 flags = []
1871 flags = []
1872 gdelta = False
1872 gdelta = False
1873 if v & revlog.REVLOGNGINLINEDATA:
1873 if v & revlog.REVLOGNGINLINEDATA:
1874 flags.append('inline')
1874 flags.append('inline')
1875 if v & revlog.REVLOGGENERALDELTA:
1875 if v & revlog.REVLOGGENERALDELTA:
1876 gdelta = True
1876 gdelta = True
1877 flags.append('generaldelta')
1877 flags.append('generaldelta')
1878 if not flags:
1878 if not flags:
1879 flags = ['(none)']
1879 flags = ['(none)']
1880
1880
1881 nummerges = 0
1881 nummerges = 0
1882 numfull = 0
1882 numfull = 0
1883 numprev = 0
1883 numprev = 0
1884 nump1 = 0
1884 nump1 = 0
1885 nump2 = 0
1885 nump2 = 0
1886 numother = 0
1886 numother = 0
1887 nump1prev = 0
1887 nump1prev = 0
1888 nump2prev = 0
1888 nump2prev = 0
1889 chainlengths = []
1889 chainlengths = []
1890
1890
1891 datasize = [None, 0, 0L]
1891 datasize = [None, 0, 0L]
1892 fullsize = [None, 0, 0L]
1892 fullsize = [None, 0, 0L]
1893 deltasize = [None, 0, 0L]
1893 deltasize = [None, 0, 0L]
1894
1894
1895 def addsize(size, l):
1895 def addsize(size, l):
1896 if l[0] is None or size < l[0]:
1896 if l[0] is None or size < l[0]:
1897 l[0] = size
1897 l[0] = size
1898 if size > l[1]:
1898 if size > l[1]:
1899 l[1] = size
1899 l[1] = size
1900 l[2] += size
1900 l[2] += size
1901
1901
1902 numrevs = len(r)
1902 numrevs = len(r)
1903 for rev in xrange(numrevs):
1903 for rev in xrange(numrevs):
1904 p1, p2 = r.parentrevs(rev)
1904 p1, p2 = r.parentrevs(rev)
1905 delta = r.deltaparent(rev)
1905 delta = r.deltaparent(rev)
1906 if format > 0:
1906 if format > 0:
1907 addsize(r.rawsize(rev), datasize)
1907 addsize(r.rawsize(rev), datasize)
1908 if p2 != nullrev:
1908 if p2 != nullrev:
1909 nummerges += 1
1909 nummerges += 1
1910 size = r.length(rev)
1910 size = r.length(rev)
1911 if delta == nullrev:
1911 if delta == nullrev:
1912 chainlengths.append(0)
1912 chainlengths.append(0)
1913 numfull += 1
1913 numfull += 1
1914 addsize(size, fullsize)
1914 addsize(size, fullsize)
1915 else:
1915 else:
1916 chainlengths.append(chainlengths[delta] + 1)
1916 chainlengths.append(chainlengths[delta] + 1)
1917 addsize(size, deltasize)
1917 addsize(size, deltasize)
1918 if delta == rev - 1:
1918 if delta == rev - 1:
1919 numprev += 1
1919 numprev += 1
1920 if delta == p1:
1920 if delta == p1:
1921 nump1prev += 1
1921 nump1prev += 1
1922 elif delta == p2:
1922 elif delta == p2:
1923 nump2prev += 1
1923 nump2prev += 1
1924 elif delta == p1:
1924 elif delta == p1:
1925 nump1 += 1
1925 nump1 += 1
1926 elif delta == p2:
1926 elif delta == p2:
1927 nump2 += 1
1927 nump2 += 1
1928 elif delta != nullrev:
1928 elif delta != nullrev:
1929 numother += 1
1929 numother += 1
1930
1930
1931 numdeltas = numrevs - numfull
1931 numdeltas = numrevs - numfull
1932 numoprev = numprev - nump1prev - nump2prev
1932 numoprev = numprev - nump1prev - nump2prev
1933 totalrawsize = datasize[2]
1933 totalrawsize = datasize[2]
1934 datasize[2] /= numrevs
1934 datasize[2] /= numrevs
1935 fulltotal = fullsize[2]
1935 fulltotal = fullsize[2]
1936 fullsize[2] /= numfull
1936 fullsize[2] /= numfull
1937 deltatotal = deltasize[2]
1937 deltatotal = deltasize[2]
1938 deltasize[2] /= numrevs - numfull
1938 deltasize[2] /= numrevs - numfull
1939 totalsize = fulltotal + deltatotal
1939 totalsize = fulltotal + deltatotal
1940 avgchainlen = sum(chainlengths) / numrevs
1940 avgchainlen = sum(chainlengths) / numrevs
1941 compratio = totalrawsize / totalsize
1941 compratio = totalrawsize / totalsize
1942
1942
1943 basedfmtstr = '%%%dd\n'
1943 basedfmtstr = '%%%dd\n'
1944 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
1944 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
1945
1945
1946 def dfmtstr(max):
1946 def dfmtstr(max):
1947 return basedfmtstr % len(str(max))
1947 return basedfmtstr % len(str(max))
1948 def pcfmtstr(max, padding=0):
1948 def pcfmtstr(max, padding=0):
1949 return basepcfmtstr % (len(str(max)), ' ' * padding)
1949 return basepcfmtstr % (len(str(max)), ' ' * padding)
1950
1950
1951 def pcfmt(value, total):
1951 def pcfmt(value, total):
1952 return (value, 100 * float(value) / total)
1952 return (value, 100 * float(value) / total)
1953
1953
1954 ui.write('format : %d\n' % format)
1954 ui.write('format : %d\n' % format)
1955 ui.write('flags : %s\n' % ', '.join(flags))
1955 ui.write('flags : %s\n' % ', '.join(flags))
1956
1956
1957 ui.write('\n')
1957 ui.write('\n')
1958 fmt = pcfmtstr(totalsize)
1958 fmt = pcfmtstr(totalsize)
1959 fmt2 = dfmtstr(totalsize)
1959 fmt2 = dfmtstr(totalsize)
1960 ui.write('revisions : ' + fmt2 % numrevs)
1960 ui.write('revisions : ' + fmt2 % numrevs)
1961 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
1961 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
1962 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
1962 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
1963 ui.write('revisions : ' + fmt2 % numrevs)
1963 ui.write('revisions : ' + fmt2 % numrevs)
1964 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
1964 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
1965 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
1965 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
1966 ui.write('revision size : ' + fmt2 % totalsize)
1966 ui.write('revision size : ' + fmt2 % totalsize)
1967 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
1967 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
1968 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
1968 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
1969
1969
1970 ui.write('\n')
1970 ui.write('\n')
1971 fmt = dfmtstr(max(avgchainlen, compratio))
1971 fmt = dfmtstr(max(avgchainlen, compratio))
1972 ui.write('avg chain length : ' + fmt % avgchainlen)
1972 ui.write('avg chain length : ' + fmt % avgchainlen)
1973 ui.write('compression ratio : ' + fmt % compratio)
1973 ui.write('compression ratio : ' + fmt % compratio)
1974
1974
1975 if format > 0:
1975 if format > 0:
1976 ui.write('\n')
1976 ui.write('\n')
1977 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
1977 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
1978 % tuple(datasize))
1978 % tuple(datasize))
1979 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
1979 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
1980 % tuple(fullsize))
1980 % tuple(fullsize))
1981 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
1981 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
1982 % tuple(deltasize))
1982 % tuple(deltasize))
1983
1983
1984 if numdeltas > 0:
1984 if numdeltas > 0:
1985 ui.write('\n')
1985 ui.write('\n')
1986 fmt = pcfmtstr(numdeltas)
1986 fmt = pcfmtstr(numdeltas)
1987 fmt2 = pcfmtstr(numdeltas, 4)
1987 fmt2 = pcfmtstr(numdeltas, 4)
1988 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
1988 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
1989 if numprev > 0:
1989 if numprev > 0:
1990 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
1990 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
1991 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
1991 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
1992 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
1992 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
1993 if gdelta:
1993 if gdelta:
1994 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
1994 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
1995 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
1995 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
1996 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
1996 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
1997
1997
1998 @command('debugrevspec', [], ('REVSPEC'))
1998 @command('debugrevspec', [], ('REVSPEC'))
1999 def debugrevspec(ui, repo, expr):
1999 def debugrevspec(ui, repo, expr):
2000 '''parse and apply a revision specification'''
2000 '''parse and apply a revision specification'''
2001 if ui.verbose:
2001 if ui.verbose:
2002 tree = revset.parse(expr)[0]
2002 tree = revset.parse(expr)[0]
2003 ui.note(tree, "\n")
2003 ui.note(tree, "\n")
2004 newtree = revset.findaliases(ui, tree)
2004 newtree = revset.findaliases(ui, tree)
2005 if newtree != tree:
2005 if newtree != tree:
2006 ui.note(newtree, "\n")
2006 ui.note(newtree, "\n")
2007 func = revset.match(ui, expr)
2007 func = revset.match(ui, expr)
2008 for c in func(repo, range(len(repo))):
2008 for c in func(repo, range(len(repo))):
2009 ui.write("%s\n" % c)
2009 ui.write("%s\n" % c)
2010
2010
2011 @command('debugsetparents', [], _('REV1 [REV2]'))
2011 @command('debugsetparents', [], _('REV1 [REV2]'))
2012 def debugsetparents(ui, repo, rev1, rev2=None):
2012 def debugsetparents(ui, repo, rev1, rev2=None):
2013 """manually set the parents of the current working directory
2013 """manually set the parents of the current working directory
2014
2014
2015 This is useful for writing repository conversion tools, but should
2015 This is useful for writing repository conversion tools, but should
2016 be used with care.
2016 be used with care.
2017
2017
2018 Returns 0 on success.
2018 Returns 0 on success.
2019 """
2019 """
2020
2020
2021 r1 = cmdutil.revsingle(repo, rev1).node()
2021 r1 = cmdutil.revsingle(repo, rev1).node()
2022 r2 = cmdutil.revsingle(repo, rev2, 'null').node()
2022 r2 = cmdutil.revsingle(repo, rev2, 'null').node()
2023
2023
2024 wlock = repo.wlock()
2024 wlock = repo.wlock()
2025 try:
2025 try:
2026 repo.dirstate.setparents(r1, r2)
2026 repo.dirstate.setparents(r1, r2)
2027 finally:
2027 finally:
2028 wlock.release()
2028 wlock.release()
2029
2029
2030 @command('debugstate',
2030 @command('debugstate',
2031 [('', 'nodates', None, _('do not display the saved mtime')),
2031 [('', 'nodates', None, _('do not display the saved mtime')),
2032 ('', 'datesort', None, _('sort by saved mtime'))],
2032 ('', 'datesort', None, _('sort by saved mtime'))],
2033 _('[OPTION]...'))
2033 _('[OPTION]...'))
2034 def debugstate(ui, repo, nodates=None, datesort=None):
2034 def debugstate(ui, repo, nodates=None, datesort=None):
2035 """show the contents of the current dirstate"""
2035 """show the contents of the current dirstate"""
2036 timestr = ""
2036 timestr = ""
2037 showdate = not nodates
2037 showdate = not nodates
2038 if datesort:
2038 if datesort:
2039 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2039 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2040 else:
2040 else:
2041 keyfunc = None # sort by filename
2041 keyfunc = None # sort by filename
2042 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2042 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2043 if showdate:
2043 if showdate:
2044 if ent[3] == -1:
2044 if ent[3] == -1:
2045 # Pad or slice to locale representation
2045 # Pad or slice to locale representation
2046 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2046 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2047 time.localtime(0)))
2047 time.localtime(0)))
2048 timestr = 'unset'
2048 timestr = 'unset'
2049 timestr = (timestr[:locale_len] +
2049 timestr = (timestr[:locale_len] +
2050 ' ' * (locale_len - len(timestr)))
2050 ' ' * (locale_len - len(timestr)))
2051 else:
2051 else:
2052 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2052 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2053 time.localtime(ent[3]))
2053 time.localtime(ent[3]))
2054 if ent[1] & 020000:
2054 if ent[1] & 020000:
2055 mode = 'lnk'
2055 mode = 'lnk'
2056 else:
2056 else:
2057 mode = '%3o' % (ent[1] & 0777)
2057 mode = '%3o' % (ent[1] & 0777)
2058 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2058 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2059 for f in repo.dirstate.copies():
2059 for f in repo.dirstate.copies():
2060 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2060 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2061
2061
2062 @command('debugsub',
2062 @command('debugsub',
2063 [('r', 'rev', '',
2063 [('r', 'rev', '',
2064 _('revision to check'), _('REV'))],
2064 _('revision to check'), _('REV'))],
2065 _('[-r REV] [REV]'))
2065 _('[-r REV] [REV]'))
2066 def debugsub(ui, repo, rev=None):
2066 def debugsub(ui, repo, rev=None):
2067 ctx = cmdutil.revsingle(repo, rev, None)
2067 ctx = cmdutil.revsingle(repo, rev, None)
2068 for k, v in sorted(ctx.substate.items()):
2068 for k, v in sorted(ctx.substate.items()):
2069 ui.write('path %s\n' % k)
2069 ui.write('path %s\n' % k)
2070 ui.write(' source %s\n' % v[0])
2070 ui.write(' source %s\n' % v[0])
2071 ui.write(' revision %s\n' % v[1])
2071 ui.write(' revision %s\n' % v[1])
2072
2072
2073 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2073 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2074 def debugwalk(ui, repo, *pats, **opts):
2074 def debugwalk(ui, repo, *pats, **opts):
2075 """show how files match on given patterns"""
2075 """show how files match on given patterns"""
2076 m = cmdutil.match(repo, pats, opts)
2076 m = cmdutil.match(repo, pats, opts)
2077 items = list(repo.walk(m))
2077 items = list(repo.walk(m))
2078 if not items:
2078 if not items:
2079 return
2079 return
2080 fmt = 'f %%-%ds %%-%ds %%s' % (
2080 fmt = 'f %%-%ds %%-%ds %%s' % (
2081 max([len(abs) for abs in items]),
2081 max([len(abs) for abs in items]),
2082 max([len(m.rel(abs)) for abs in items]))
2082 max([len(m.rel(abs)) for abs in items]))
2083 for abs in items:
2083 for abs in items:
2084 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2084 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2085 ui.write("%s\n" % line.rstrip())
2085 ui.write("%s\n" % line.rstrip())
2086
2086
2087 @command('debugwireargs',
2087 @command('debugwireargs',
2088 [('', 'three', '', 'three'),
2088 [('', 'three', '', 'three'),
2089 ('', 'four', '', 'four'),
2089 ('', 'four', '', 'four'),
2090 ('', 'five', '', 'five'),
2090 ('', 'five', '', 'five'),
2091 ] + remoteopts,
2091 ] + remoteopts,
2092 _('REPO [OPTIONS]... [ONE [TWO]]'))
2092 _('REPO [OPTIONS]... [ONE [TWO]]'))
2093 def debugwireargs(ui, repopath, *vals, **opts):
2093 def debugwireargs(ui, repopath, *vals, **opts):
2094 repo = hg.repository(hg.remoteui(ui, opts), repopath)
2094 repo = hg.repository(hg.remoteui(ui, opts), repopath)
2095 for opt in remoteopts:
2095 for opt in remoteopts:
2096 del opts[opt[1]]
2096 del opts[opt[1]]
2097 args = {}
2097 args = {}
2098 for k, v in opts.iteritems():
2098 for k, v in opts.iteritems():
2099 if v:
2099 if v:
2100 args[k] = v
2100 args[k] = v
2101 # run twice to check that we don't mess up the stream for the next command
2101 # run twice to check that we don't mess up the stream for the next command
2102 res1 = repo.debugwireargs(*vals, **args)
2102 res1 = repo.debugwireargs(*vals, **args)
2103 res2 = repo.debugwireargs(*vals, **args)
2103 res2 = repo.debugwireargs(*vals, **args)
2104 ui.write("%s\n" % res1)
2104 ui.write("%s\n" % res1)
2105 if res1 != res2:
2105 if res1 != res2:
2106 ui.warn("%s\n" % res2)
2106 ui.warn("%s\n" % res2)
2107
2107
2108 @command('^diff',
2108 @command('^diff',
2109 [('r', 'rev', [], _('revision'), _('REV')),
2109 [('r', 'rev', [], _('revision'), _('REV')),
2110 ('c', 'change', '', _('change made by revision'), _('REV'))
2110 ('c', 'change', '', _('change made by revision'), _('REV'))
2111 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2111 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2112 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2112 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2113 def diff(ui, repo, *pats, **opts):
2113 def diff(ui, repo, *pats, **opts):
2114 """diff repository (or selected files)
2114 """diff repository (or selected files)
2115
2115
2116 Show differences between revisions for the specified files.
2116 Show differences between revisions for the specified files.
2117
2117
2118 Differences between files are shown using the unified diff format.
2118 Differences between files are shown using the unified diff format.
2119
2119
2120 .. note::
2120 .. note::
2121 diff may generate unexpected results for merges, as it will
2121 diff may generate unexpected results for merges, as it will
2122 default to comparing against the working directory's first
2122 default to comparing against the working directory's first
2123 parent changeset if no revisions are specified.
2123 parent changeset if no revisions are specified.
2124
2124
2125 When two revision arguments are given, then changes are shown
2125 When two revision arguments are given, then changes are shown
2126 between those revisions. If only one revision is specified then
2126 between those revisions. If only one revision is specified then
2127 that revision is compared to the working directory, and, when no
2127 that revision is compared to the working directory, and, when no
2128 revisions are specified, the working directory files are compared
2128 revisions are specified, the working directory files are compared
2129 to its parent.
2129 to its parent.
2130
2130
2131 Alternatively you can specify -c/--change with a revision to see
2131 Alternatively you can specify -c/--change with a revision to see
2132 the changes in that changeset relative to its first parent.
2132 the changes in that changeset relative to its first parent.
2133
2133
2134 Without the -a/--text option, diff will avoid generating diffs of
2134 Without the -a/--text option, diff will avoid generating diffs of
2135 files it detects as binary. With -a, diff will generate a diff
2135 files it detects as binary. With -a, diff will generate a diff
2136 anyway, probably with undesirable results.
2136 anyway, probably with undesirable results.
2137
2137
2138 Use the -g/--git option to generate diffs in the git extended diff
2138 Use the -g/--git option to generate diffs in the git extended diff
2139 format. For more information, read :hg:`help diffs`.
2139 format. For more information, read :hg:`help diffs`.
2140
2140
2141 Returns 0 on success.
2141 Returns 0 on success.
2142 """
2142 """
2143
2143
2144 revs = opts.get('rev')
2144 revs = opts.get('rev')
2145 change = opts.get('change')
2145 change = opts.get('change')
2146 stat = opts.get('stat')
2146 stat = opts.get('stat')
2147 reverse = opts.get('reverse')
2147 reverse = opts.get('reverse')
2148
2148
2149 if revs and change:
2149 if revs and change:
2150 msg = _('cannot specify --rev and --change at the same time')
2150 msg = _('cannot specify --rev and --change at the same time')
2151 raise util.Abort(msg)
2151 raise util.Abort(msg)
2152 elif change:
2152 elif change:
2153 node2 = cmdutil.revsingle(repo, change, None).node()
2153 node2 = cmdutil.revsingle(repo, change, None).node()
2154 node1 = repo[node2].p1().node()
2154 node1 = repo[node2].p1().node()
2155 else:
2155 else:
2156 node1, node2 = cmdutil.revpair(repo, revs)
2156 node1, node2 = cmdutil.revpair(repo, revs)
2157
2157
2158 if reverse:
2158 if reverse:
2159 node1, node2 = node2, node1
2159 node1, node2 = node2, node1
2160
2160
2161 diffopts = patch.diffopts(ui, opts)
2161 diffopts = patch.diffopts(ui, opts)
2162 m = cmdutil.match(repo, pats, opts)
2162 m = cmdutil.match(repo, pats, opts)
2163 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2163 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2164 listsubrepos=opts.get('subrepos'))
2164 listsubrepos=opts.get('subrepos'))
2165
2165
2166 @command('^export',
2166 @command('^export',
2167 [('o', 'output', '',
2167 [('o', 'output', '',
2168 _('print output to file with formatted name'), _('FORMAT')),
2168 _('print output to file with formatted name'), _('FORMAT')),
2169 ('', 'switch-parent', None, _('diff against the second parent')),
2169 ('', 'switch-parent', None, _('diff against the second parent')),
2170 ('r', 'rev', [], _('revisions to export'), _('REV')),
2170 ('r', 'rev', [], _('revisions to export'), _('REV')),
2171 ] + diffopts,
2171 ] + diffopts,
2172 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2172 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2173 def export(ui, repo, *changesets, **opts):
2173 def export(ui, repo, *changesets, **opts):
2174 """dump the header and diffs for one or more changesets
2174 """dump the header and diffs for one or more changesets
2175
2175
2176 Print the changeset header and diffs for one or more revisions.
2176 Print the changeset header and diffs for one or more revisions.
2177
2177
2178 The information shown in the changeset header is: author, date,
2178 The information shown in the changeset header is: author, date,
2179 branch name (if non-default), changeset hash, parent(s) and commit
2179 branch name (if non-default), changeset hash, parent(s) and commit
2180 comment.
2180 comment.
2181
2181
2182 .. note::
2182 .. note::
2183 export may generate unexpected diff output for merge
2183 export may generate unexpected diff output for merge
2184 changesets, as it will compare the merge changeset against its
2184 changesets, as it will compare the merge changeset against its
2185 first parent only.
2185 first parent only.
2186
2186
2187 Output may be to a file, in which case the name of the file is
2187 Output may be to a file, in which case the name of the file is
2188 given using a format string. The formatting rules are as follows:
2188 given using a format string. The formatting rules are as follows:
2189
2189
2190 :``%%``: literal "%" character
2190 :``%%``: literal "%" character
2191 :``%H``: changeset hash (40 hexadecimal digits)
2191 :``%H``: changeset hash (40 hexadecimal digits)
2192 :``%N``: number of patches being generated
2192 :``%N``: number of patches being generated
2193 :``%R``: changeset revision number
2193 :``%R``: changeset revision number
2194 :``%b``: basename of the exporting repository
2194 :``%b``: basename of the exporting repository
2195 :``%h``: short-form changeset hash (12 hexadecimal digits)
2195 :``%h``: short-form changeset hash (12 hexadecimal digits)
2196 :``%n``: zero-padded sequence number, starting at 1
2196 :``%n``: zero-padded sequence number, starting at 1
2197 :``%r``: zero-padded changeset revision number
2197 :``%r``: zero-padded changeset revision number
2198
2198
2199 Without the -a/--text option, export will avoid generating diffs
2199 Without the -a/--text option, export will avoid generating diffs
2200 of files it detects as binary. With -a, export will generate a
2200 of files it detects as binary. With -a, export will generate a
2201 diff anyway, probably with undesirable results.
2201 diff anyway, probably with undesirable results.
2202
2202
2203 Use the -g/--git option to generate diffs in the git extended diff
2203 Use the -g/--git option to generate diffs in the git extended diff
2204 format. See :hg:`help diffs` for more information.
2204 format. See :hg:`help diffs` for more information.
2205
2205
2206 With the --switch-parent option, the diff will be against the
2206 With the --switch-parent option, the diff will be against the
2207 second parent. It can be useful to review a merge.
2207 second parent. It can be useful to review a merge.
2208
2208
2209 Returns 0 on success.
2209 Returns 0 on success.
2210 """
2210 """
2211 changesets += tuple(opts.get('rev', []))
2211 changesets += tuple(opts.get('rev', []))
2212 if not changesets:
2212 if not changesets:
2213 raise util.Abort(_("export requires at least one changeset"))
2213 raise util.Abort(_("export requires at least one changeset"))
2214 revs = cmdutil.revrange(repo, changesets)
2214 revs = cmdutil.revrange(repo, changesets)
2215 if len(revs) > 1:
2215 if len(revs) > 1:
2216 ui.note(_('exporting patches:\n'))
2216 ui.note(_('exporting patches:\n'))
2217 else:
2217 else:
2218 ui.note(_('exporting patch:\n'))
2218 ui.note(_('exporting patch:\n'))
2219 cmdutil.export(repo, revs, template=opts.get('output'),
2219 cmdutil.export(repo, revs, template=opts.get('output'),
2220 switch_parent=opts.get('switch_parent'),
2220 switch_parent=opts.get('switch_parent'),
2221 opts=patch.diffopts(ui, opts))
2221 opts=patch.diffopts(ui, opts))
2222
2222
2223 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2223 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2224 def forget(ui, repo, *pats, **opts):
2224 def forget(ui, repo, *pats, **opts):
2225 """forget the specified files on the next commit
2225 """forget the specified files on the next commit
2226
2226
2227 Mark the specified files so they will no longer be tracked
2227 Mark the specified files so they will no longer be tracked
2228 after the next commit.
2228 after the next commit.
2229
2229
2230 This only removes files from the current branch, not from the
2230 This only removes files from the current branch, not from the
2231 entire project history, and it does not delete them from the
2231 entire project history, and it does not delete them from the
2232 working directory.
2232 working directory.
2233
2233
2234 To undo a forget before the next commit, see :hg:`add`.
2234 To undo a forget before the next commit, see :hg:`add`.
2235
2235
2236 Returns 0 on success.
2236 Returns 0 on success.
2237 """
2237 """
2238
2238
2239 if not pats:
2239 if not pats:
2240 raise util.Abort(_('no files specified'))
2240 raise util.Abort(_('no files specified'))
2241
2241
2242 m = cmdutil.match(repo, pats, opts)
2242 m = cmdutil.match(repo, pats, opts)
2243 s = repo.status(match=m, clean=True)
2243 s = repo.status(match=m, clean=True)
2244 forget = sorted(s[0] + s[1] + s[3] + s[6])
2244 forget = sorted(s[0] + s[1] + s[3] + s[6])
2245 errs = 0
2245 errs = 0
2246
2246
2247 for f in m.files():
2247 for f in m.files():
2248 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2248 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2249 ui.warn(_('not removing %s: file is already untracked\n')
2249 ui.warn(_('not removing %s: file is already untracked\n')
2250 % m.rel(f))
2250 % m.rel(f))
2251 errs = 1
2251 errs = 1
2252
2252
2253 for f in forget:
2253 for f in forget:
2254 if ui.verbose or not m.exact(f):
2254 if ui.verbose or not m.exact(f):
2255 ui.status(_('removing %s\n') % m.rel(f))
2255 ui.status(_('removing %s\n') % m.rel(f))
2256
2256
2257 repo[None].remove(forget, unlink=False)
2257 repo[None].remove(forget, unlink=False)
2258 return errs
2258 return errs
2259
2259
2260 @command('grep',
2260 @command('grep',
2261 [('0', 'print0', None, _('end fields with NUL')),
2261 [('0', 'print0', None, _('end fields with NUL')),
2262 ('', 'all', None, _('print all revisions that match')),
2262 ('', 'all', None, _('print all revisions that match')),
2263 ('a', 'text', None, _('treat all files as text')),
2263 ('a', 'text', None, _('treat all files as text')),
2264 ('f', 'follow', None,
2264 ('f', 'follow', None,
2265 _('follow changeset history,'
2265 _('follow changeset history,'
2266 ' or file history across copies and renames')),
2266 ' or file history across copies and renames')),
2267 ('i', 'ignore-case', None, _('ignore case when matching')),
2267 ('i', 'ignore-case', None, _('ignore case when matching')),
2268 ('l', 'files-with-matches', None,
2268 ('l', 'files-with-matches', None,
2269 _('print only filenames and revisions that match')),
2269 _('print only filenames and revisions that match')),
2270 ('n', 'line-number', None, _('print matching line numbers')),
2270 ('n', 'line-number', None, _('print matching line numbers')),
2271 ('r', 'rev', [],
2271 ('r', 'rev', [],
2272 _('only search files changed within revision range'), _('REV')),
2272 _('only search files changed within revision range'), _('REV')),
2273 ('u', 'user', None, _('list the author (long with -v)')),
2273 ('u', 'user', None, _('list the author (long with -v)')),
2274 ('d', 'date', None, _('list the date (short with -q)')),
2274 ('d', 'date', None, _('list the date (short with -q)')),
2275 ] + walkopts,
2275 ] + walkopts,
2276 _('[OPTION]... PATTERN [FILE]...'))
2276 _('[OPTION]... PATTERN [FILE]...'))
2277 def grep(ui, repo, pattern, *pats, **opts):
2277 def grep(ui, repo, pattern, *pats, **opts):
2278 """search for a pattern in specified files and revisions
2278 """search for a pattern in specified files and revisions
2279
2279
2280 Search revisions of files for a regular expression.
2280 Search revisions of files for a regular expression.
2281
2281
2282 This command behaves differently than Unix grep. It only accepts
2282 This command behaves differently than Unix grep. It only accepts
2283 Python/Perl regexps. It searches repository history, not the
2283 Python/Perl regexps. It searches repository history, not the
2284 working directory. It always prints the revision number in which a
2284 working directory. It always prints the revision number in which a
2285 match appears.
2285 match appears.
2286
2286
2287 By default, grep only prints output for the first revision of a
2287 By default, grep only prints output for the first revision of a
2288 file in which it finds a match. To get it to print every revision
2288 file in which it finds a match. To get it to print every revision
2289 that contains a change in match status ("-" for a match that
2289 that contains a change in match status ("-" for a match that
2290 becomes a non-match, or "+" for a non-match that becomes a match),
2290 becomes a non-match, or "+" for a non-match that becomes a match),
2291 use the --all flag.
2291 use the --all flag.
2292
2292
2293 Returns 0 if a match is found, 1 otherwise.
2293 Returns 0 if a match is found, 1 otherwise.
2294 """
2294 """
2295 reflags = 0
2295 reflags = 0
2296 if opts.get('ignore_case'):
2296 if opts.get('ignore_case'):
2297 reflags |= re.I
2297 reflags |= re.I
2298 try:
2298 try:
2299 regexp = re.compile(pattern, reflags)
2299 regexp = re.compile(pattern, reflags)
2300 except re.error, inst:
2300 except re.error, inst:
2301 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2301 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2302 return 1
2302 return 1
2303 sep, eol = ':', '\n'
2303 sep, eol = ':', '\n'
2304 if opts.get('print0'):
2304 if opts.get('print0'):
2305 sep = eol = '\0'
2305 sep = eol = '\0'
2306
2306
2307 getfile = util.lrucachefunc(repo.file)
2307 getfile = util.lrucachefunc(repo.file)
2308
2308
2309 def matchlines(body):
2309 def matchlines(body):
2310 begin = 0
2310 begin = 0
2311 linenum = 0
2311 linenum = 0
2312 while True:
2312 while True:
2313 match = regexp.search(body, begin)
2313 match = regexp.search(body, begin)
2314 if not match:
2314 if not match:
2315 break
2315 break
2316 mstart, mend = match.span()
2316 mstart, mend = match.span()
2317 linenum += body.count('\n', begin, mstart) + 1
2317 linenum += body.count('\n', begin, mstart) + 1
2318 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2318 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2319 begin = body.find('\n', mend) + 1 or len(body)
2319 begin = body.find('\n', mend) + 1 or len(body)
2320 lend = begin - 1
2320 lend = begin - 1
2321 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2321 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2322
2322
2323 class linestate(object):
2323 class linestate(object):
2324 def __init__(self, line, linenum, colstart, colend):
2324 def __init__(self, line, linenum, colstart, colend):
2325 self.line = line
2325 self.line = line
2326 self.linenum = linenum
2326 self.linenum = linenum
2327 self.colstart = colstart
2327 self.colstart = colstart
2328 self.colend = colend
2328 self.colend = colend
2329
2329
2330 def __hash__(self):
2330 def __hash__(self):
2331 return hash((self.linenum, self.line))
2331 return hash((self.linenum, self.line))
2332
2332
2333 def __eq__(self, other):
2333 def __eq__(self, other):
2334 return self.line == other.line
2334 return self.line == other.line
2335
2335
2336 matches = {}
2336 matches = {}
2337 copies = {}
2337 copies = {}
2338 def grepbody(fn, rev, body):
2338 def grepbody(fn, rev, body):
2339 matches[rev].setdefault(fn, [])
2339 matches[rev].setdefault(fn, [])
2340 m = matches[rev][fn]
2340 m = matches[rev][fn]
2341 for lnum, cstart, cend, line in matchlines(body):
2341 for lnum, cstart, cend, line in matchlines(body):
2342 s = linestate(line, lnum, cstart, cend)
2342 s = linestate(line, lnum, cstart, cend)
2343 m.append(s)
2343 m.append(s)
2344
2344
2345 def difflinestates(a, b):
2345 def difflinestates(a, b):
2346 sm = difflib.SequenceMatcher(None, a, b)
2346 sm = difflib.SequenceMatcher(None, a, b)
2347 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2347 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2348 if tag == 'insert':
2348 if tag == 'insert':
2349 for i in xrange(blo, bhi):
2349 for i in xrange(blo, bhi):
2350 yield ('+', b[i])
2350 yield ('+', b[i])
2351 elif tag == 'delete':
2351 elif tag == 'delete':
2352 for i in xrange(alo, ahi):
2352 for i in xrange(alo, ahi):
2353 yield ('-', a[i])
2353 yield ('-', a[i])
2354 elif tag == 'replace':
2354 elif tag == 'replace':
2355 for i in xrange(alo, ahi):
2355 for i in xrange(alo, ahi):
2356 yield ('-', a[i])
2356 yield ('-', a[i])
2357 for i in xrange(blo, bhi):
2357 for i in xrange(blo, bhi):
2358 yield ('+', b[i])
2358 yield ('+', b[i])
2359
2359
2360 def display(fn, ctx, pstates, states):
2360 def display(fn, ctx, pstates, states):
2361 rev = ctx.rev()
2361 rev = ctx.rev()
2362 datefunc = ui.quiet and util.shortdate or util.datestr
2362 datefunc = ui.quiet and util.shortdate or util.datestr
2363 found = False
2363 found = False
2364 filerevmatches = {}
2364 filerevmatches = {}
2365 def binary():
2365 def binary():
2366 flog = getfile(fn)
2366 flog = getfile(fn)
2367 return util.binary(flog.read(ctx.filenode(fn)))
2367 return util.binary(flog.read(ctx.filenode(fn)))
2368
2368
2369 if opts.get('all'):
2369 if opts.get('all'):
2370 iter = difflinestates(pstates, states)
2370 iter = difflinestates(pstates, states)
2371 else:
2371 else:
2372 iter = [('', l) for l in states]
2372 iter = [('', l) for l in states]
2373 for change, l in iter:
2373 for change, l in iter:
2374 cols = [fn, str(rev)]
2374 cols = [fn, str(rev)]
2375 before, match, after = None, None, None
2375 before, match, after = None, None, None
2376 if opts.get('line_number'):
2376 if opts.get('line_number'):
2377 cols.append(str(l.linenum))
2377 cols.append(str(l.linenum))
2378 if opts.get('all'):
2378 if opts.get('all'):
2379 cols.append(change)
2379 cols.append(change)
2380 if opts.get('user'):
2380 if opts.get('user'):
2381 cols.append(ui.shortuser(ctx.user()))
2381 cols.append(ui.shortuser(ctx.user()))
2382 if opts.get('date'):
2382 if opts.get('date'):
2383 cols.append(datefunc(ctx.date()))
2383 cols.append(datefunc(ctx.date()))
2384 if opts.get('files_with_matches'):
2384 if opts.get('files_with_matches'):
2385 c = (fn, rev)
2385 c = (fn, rev)
2386 if c in filerevmatches:
2386 if c in filerevmatches:
2387 continue
2387 continue
2388 filerevmatches[c] = 1
2388 filerevmatches[c] = 1
2389 else:
2389 else:
2390 before = l.line[:l.colstart]
2390 before = l.line[:l.colstart]
2391 match = l.line[l.colstart:l.colend]
2391 match = l.line[l.colstart:l.colend]
2392 after = l.line[l.colend:]
2392 after = l.line[l.colend:]
2393 ui.write(sep.join(cols))
2393 ui.write(sep.join(cols))
2394 if before is not None:
2394 if before is not None:
2395 if not opts.get('text') and binary():
2395 if not opts.get('text') and binary():
2396 ui.write(sep + " Binary file matches")
2396 ui.write(sep + " Binary file matches")
2397 else:
2397 else:
2398 ui.write(sep + before)
2398 ui.write(sep + before)
2399 ui.write(match, label='grep.match')
2399 ui.write(match, label='grep.match')
2400 ui.write(after)
2400 ui.write(after)
2401 ui.write(eol)
2401 ui.write(eol)
2402 found = True
2402 found = True
2403 return found
2403 return found
2404
2404
2405 skip = {}
2405 skip = {}
2406 revfiles = {}
2406 revfiles = {}
2407 matchfn = cmdutil.match(repo, pats, opts)
2407 matchfn = cmdutil.match(repo, pats, opts)
2408 found = False
2408 found = False
2409 follow = opts.get('follow')
2409 follow = opts.get('follow')
2410
2410
2411 def prep(ctx, fns):
2411 def prep(ctx, fns):
2412 rev = ctx.rev()
2412 rev = ctx.rev()
2413 pctx = ctx.p1()
2413 pctx = ctx.p1()
2414 parent = pctx.rev()
2414 parent = pctx.rev()
2415 matches.setdefault(rev, {})
2415 matches.setdefault(rev, {})
2416 matches.setdefault(parent, {})
2416 matches.setdefault(parent, {})
2417 files = revfiles.setdefault(rev, [])
2417 files = revfiles.setdefault(rev, [])
2418 for fn in fns:
2418 for fn in fns:
2419 flog = getfile(fn)
2419 flog = getfile(fn)
2420 try:
2420 try:
2421 fnode = ctx.filenode(fn)
2421 fnode = ctx.filenode(fn)
2422 except error.LookupError:
2422 except error.LookupError:
2423 continue
2423 continue
2424
2424
2425 copied = flog.renamed(fnode)
2425 copied = flog.renamed(fnode)
2426 copy = follow and copied and copied[0]
2426 copy = follow and copied and copied[0]
2427 if copy:
2427 if copy:
2428 copies.setdefault(rev, {})[fn] = copy
2428 copies.setdefault(rev, {})[fn] = copy
2429 if fn in skip:
2429 if fn in skip:
2430 if copy:
2430 if copy:
2431 skip[copy] = True
2431 skip[copy] = True
2432 continue
2432 continue
2433 files.append(fn)
2433 files.append(fn)
2434
2434
2435 if fn not in matches[rev]:
2435 if fn not in matches[rev]:
2436 grepbody(fn, rev, flog.read(fnode))
2436 grepbody(fn, rev, flog.read(fnode))
2437
2437
2438 pfn = copy or fn
2438 pfn = copy or fn
2439 if pfn not in matches[parent]:
2439 if pfn not in matches[parent]:
2440 try:
2440 try:
2441 fnode = pctx.filenode(pfn)
2441 fnode = pctx.filenode(pfn)
2442 grepbody(pfn, parent, flog.read(fnode))
2442 grepbody(pfn, parent, flog.read(fnode))
2443 except error.LookupError:
2443 except error.LookupError:
2444 pass
2444 pass
2445
2445
2446 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2446 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2447 rev = ctx.rev()
2447 rev = ctx.rev()
2448 parent = ctx.p1().rev()
2448 parent = ctx.p1().rev()
2449 for fn in sorted(revfiles.get(rev, [])):
2449 for fn in sorted(revfiles.get(rev, [])):
2450 states = matches[rev][fn]
2450 states = matches[rev][fn]
2451 copy = copies.get(rev, {}).get(fn)
2451 copy = copies.get(rev, {}).get(fn)
2452 if fn in skip:
2452 if fn in skip:
2453 if copy:
2453 if copy:
2454 skip[copy] = True
2454 skip[copy] = True
2455 continue
2455 continue
2456 pstates = matches.get(parent, {}).get(copy or fn, [])
2456 pstates = matches.get(parent, {}).get(copy or fn, [])
2457 if pstates or states:
2457 if pstates or states:
2458 r = display(fn, ctx, pstates, states)
2458 r = display(fn, ctx, pstates, states)
2459 found = found or r
2459 found = found or r
2460 if r and not opts.get('all'):
2460 if r and not opts.get('all'):
2461 skip[fn] = True
2461 skip[fn] = True
2462 if copy:
2462 if copy:
2463 skip[copy] = True
2463 skip[copy] = True
2464 del matches[rev]
2464 del matches[rev]
2465 del revfiles[rev]
2465 del revfiles[rev]
2466
2466
2467 return not found
2467 return not found
2468
2468
2469 @command('heads',
2469 @command('heads',
2470 [('r', 'rev', '',
2470 [('r', 'rev', '',
2471 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2471 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2472 ('t', 'topo', False, _('show topological heads only')),
2472 ('t', 'topo', False, _('show topological heads only')),
2473 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2473 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2474 ('c', 'closed', False, _('show normal and closed branch heads')),
2474 ('c', 'closed', False, _('show normal and closed branch heads')),
2475 ] + templateopts,
2475 ] + templateopts,
2476 _('[-ac] [-r STARTREV] [REV]...'))
2476 _('[-ac] [-r STARTREV] [REV]...'))
2477 def heads(ui, repo, *branchrevs, **opts):
2477 def heads(ui, repo, *branchrevs, **opts):
2478 """show current repository heads or show branch heads
2478 """show current repository heads or show branch heads
2479
2479
2480 With no arguments, show all repository branch heads.
2480 With no arguments, show all repository branch heads.
2481
2481
2482 Repository "heads" are changesets with no child changesets. They are
2482 Repository "heads" are changesets with no child changesets. They are
2483 where development generally takes place and are the usual targets
2483 where development generally takes place and are the usual targets
2484 for update and merge operations. Branch heads are changesets that have
2484 for update and merge operations. Branch heads are changesets that have
2485 no child changeset on the same branch.
2485 no child changeset on the same branch.
2486
2486
2487 If one or more REVs are given, only branch heads on the branches
2487 If one or more REVs are given, only branch heads on the branches
2488 associated with the specified changesets are shown.
2488 associated with the specified changesets are shown.
2489
2489
2490 If -c/--closed is specified, also show branch heads marked closed
2490 If -c/--closed is specified, also show branch heads marked closed
2491 (see :hg:`commit --close-branch`).
2491 (see :hg:`commit --close-branch`).
2492
2492
2493 If STARTREV is specified, only those heads that are descendants of
2493 If STARTREV is specified, only those heads that are descendants of
2494 STARTREV will be displayed.
2494 STARTREV will be displayed.
2495
2495
2496 If -t/--topo is specified, named branch mechanics will be ignored and only
2496 If -t/--topo is specified, named branch mechanics will be ignored and only
2497 changesets without children will be shown.
2497 changesets without children will be shown.
2498
2498
2499 Returns 0 if matching heads are found, 1 if not.
2499 Returns 0 if matching heads are found, 1 if not.
2500 """
2500 """
2501
2501
2502 start = None
2502 start = None
2503 if 'rev' in opts:
2503 if 'rev' in opts:
2504 start = cmdutil.revsingle(repo, opts['rev'], None).node()
2504 start = cmdutil.revsingle(repo, opts['rev'], None).node()
2505
2505
2506 if opts.get('topo'):
2506 if opts.get('topo'):
2507 heads = [repo[h] for h in repo.heads(start)]
2507 heads = [repo[h] for h in repo.heads(start)]
2508 else:
2508 else:
2509 heads = []
2509 heads = []
2510 for b, ls in repo.branchmap().iteritems():
2510 for b, ls in repo.branchmap().iteritems():
2511 if start is None:
2511 if start is None:
2512 heads += [repo[h] for h in ls]
2512 heads += [repo[h] for h in ls]
2513 continue
2513 continue
2514 startrev = repo.changelog.rev(start)
2514 startrev = repo.changelog.rev(start)
2515 descendants = set(repo.changelog.descendants(startrev))
2515 descendants = set(repo.changelog.descendants(startrev))
2516 descendants.add(startrev)
2516 descendants.add(startrev)
2517 rev = repo.changelog.rev
2517 rev = repo.changelog.rev
2518 heads += [repo[h] for h in ls if rev(h) in descendants]
2518 heads += [repo[h] for h in ls if rev(h) in descendants]
2519
2519
2520 if branchrevs:
2520 if branchrevs:
2521 branches = set(repo[br].branch() for br in branchrevs)
2521 branches = set(repo[br].branch() for br in branchrevs)
2522 heads = [h for h in heads if h.branch() in branches]
2522 heads = [h for h in heads if h.branch() in branches]
2523
2523
2524 if not opts.get('closed'):
2524 if not opts.get('closed'):
2525 heads = [h for h in heads if not h.extra().get('close')]
2525 heads = [h for h in heads if not h.extra().get('close')]
2526
2526
2527 if opts.get('active') and branchrevs:
2527 if opts.get('active') and branchrevs:
2528 dagheads = repo.heads(start)
2528 dagheads = repo.heads(start)
2529 heads = [h for h in heads if h.node() in dagheads]
2529 heads = [h for h in heads if h.node() in dagheads]
2530
2530
2531 if branchrevs:
2531 if branchrevs:
2532 haveheads = set(h.branch() for h in heads)
2532 haveheads = set(h.branch() for h in heads)
2533 if branches - haveheads:
2533 if branches - haveheads:
2534 headless = ', '.join(b for b in branches - haveheads)
2534 headless = ', '.join(b for b in branches - haveheads)
2535 msg = _('no open branch heads found on branches %s')
2535 msg = _('no open branch heads found on branches %s')
2536 if opts.get('rev'):
2536 if opts.get('rev'):
2537 msg += _(' (started at %s)' % opts['rev'])
2537 msg += _(' (started at %s)' % opts['rev'])
2538 ui.warn((msg + '\n') % headless)
2538 ui.warn((msg + '\n') % headless)
2539
2539
2540 if not heads:
2540 if not heads:
2541 return 1
2541 return 1
2542
2542
2543 heads = sorted(heads, key=lambda x: -x.rev())
2543 heads = sorted(heads, key=lambda x: -x.rev())
2544 displayer = cmdutil.show_changeset(ui, repo, opts)
2544 displayer = cmdutil.show_changeset(ui, repo, opts)
2545 for ctx in heads:
2545 for ctx in heads:
2546 displayer.show(ctx)
2546 displayer.show(ctx)
2547 displayer.close()
2547 displayer.close()
2548
2548
2549 @command('help',
2549 @command('help',
2550 [('e', 'extension', None, _('show only help for extensions')),
2550 [('e', 'extension', None, _('show only help for extensions')),
2551 ('c', 'command', None, _('show only help for commands'))],
2551 ('c', 'command', None, _('show only help for commands'))],
2552 _('[-ec] [TOPIC]'))
2552 _('[-ec] [TOPIC]'))
2553 def help_(ui, name=None, with_version=False, unknowncmd=False, full=True, **opts):
2553 def help_(ui, name=None, with_version=False, unknowncmd=False, full=True, **opts):
2554 """show help for a given topic or a help overview
2554 """show help for a given topic or a help overview
2555
2555
2556 With no arguments, print a list of commands with short help messages.
2556 With no arguments, print a list of commands with short help messages.
2557
2557
2558 Given a topic, extension, or command name, print help for that
2558 Given a topic, extension, or command name, print help for that
2559 topic.
2559 topic.
2560
2560
2561 Returns 0 if successful.
2561 Returns 0 if successful.
2562 """
2562 """
2563 option_lists = []
2563 option_lists = []
2564 textwidth = min(ui.termwidth(), 80) - 2
2564 textwidth = min(ui.termwidth(), 80) - 2
2565
2565
2566 def addglobalopts(aliases):
2566 def addglobalopts(aliases):
2567 if ui.verbose:
2567 if ui.verbose:
2568 option_lists.append((_("global options:"), globalopts))
2568 option_lists.append((_("global options:"), globalopts))
2569 if name == 'shortlist':
2569 if name == 'shortlist':
2570 option_lists.append((_('use "hg help" for the full list '
2570 option_lists.append((_('use "hg help" for the full list '
2571 'of commands'), ()))
2571 'of commands'), ()))
2572 else:
2572 else:
2573 if name == 'shortlist':
2573 if name == 'shortlist':
2574 msg = _('use "hg help" for the full list of commands '
2574 msg = _('use "hg help" for the full list of commands '
2575 'or "hg -v" for details')
2575 'or "hg -v" for details')
2576 elif name and not full:
2576 elif name and not full:
2577 msg = _('use "hg help %s" to show the full help text' % name)
2577 msg = _('use "hg help %s" to show the full help text' % name)
2578 elif aliases:
2578 elif aliases:
2579 msg = _('use "hg -v help%s" to show builtin aliases and '
2579 msg = _('use "hg -v help%s" to show builtin aliases and '
2580 'global options') % (name and " " + name or "")
2580 'global options') % (name and " " + name or "")
2581 else:
2581 else:
2582 msg = _('use "hg -v help %s" to show global options') % name
2582 msg = _('use "hg -v help %s" to show global options') % name
2583 option_lists.append((msg, ()))
2583 option_lists.append((msg, ()))
2584
2584
2585 def helpcmd(name):
2585 def helpcmd(name):
2586 if with_version:
2586 if with_version:
2587 version_(ui)
2587 version_(ui)
2588 ui.write('\n')
2588 ui.write('\n')
2589
2589
2590 try:
2590 try:
2591 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
2591 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
2592 except error.AmbiguousCommand, inst:
2592 except error.AmbiguousCommand, inst:
2593 # py3k fix: except vars can't be used outside the scope of the
2593 # py3k fix: except vars can't be used outside the scope of the
2594 # except block, nor can be used inside a lambda. python issue4617
2594 # except block, nor can be used inside a lambda. python issue4617
2595 prefix = inst.args[0]
2595 prefix = inst.args[0]
2596 select = lambda c: c.lstrip('^').startswith(prefix)
2596 select = lambda c: c.lstrip('^').startswith(prefix)
2597 helplist(_('list of commands:\n\n'), select)
2597 helplist(_('list of commands:\n\n'), select)
2598 return
2598 return
2599
2599
2600 # check if it's an invalid alias and display its error if it is
2600 # check if it's an invalid alias and display its error if it is
2601 if getattr(entry[0], 'badalias', False):
2601 if getattr(entry[0], 'badalias', False):
2602 if not unknowncmd:
2602 if not unknowncmd:
2603 entry[0](ui)
2603 entry[0](ui)
2604 return
2604 return
2605
2605
2606 # synopsis
2606 # synopsis
2607 if len(entry) > 2:
2607 if len(entry) > 2:
2608 if entry[2].startswith('hg'):
2608 if entry[2].startswith('hg'):
2609 ui.write("%s\n" % entry[2])
2609 ui.write("%s\n" % entry[2])
2610 else:
2610 else:
2611 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
2611 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
2612 else:
2612 else:
2613 ui.write('hg %s\n' % aliases[0])
2613 ui.write('hg %s\n' % aliases[0])
2614
2614
2615 # aliases
2615 # aliases
2616 if full and not ui.quiet and len(aliases) > 1:
2616 if full and not ui.quiet and len(aliases) > 1:
2617 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
2617 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
2618
2618
2619 # description
2619 # description
2620 doc = gettext(entry[0].__doc__)
2620 doc = gettext(entry[0].__doc__)
2621 if not doc:
2621 if not doc:
2622 doc = _("(no help text available)")
2622 doc = _("(no help text available)")
2623 if hasattr(entry[0], 'definition'): # aliased command
2623 if hasattr(entry[0], 'definition'): # aliased command
2624 if entry[0].definition.startswith('!'): # shell alias
2624 if entry[0].definition.startswith('!'): # shell alias
2625 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
2625 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
2626 else:
2626 else:
2627 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
2627 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
2628 if ui.quiet or not full:
2628 if ui.quiet or not full:
2629 doc = doc.splitlines()[0]
2629 doc = doc.splitlines()[0]
2630 keep = ui.verbose and ['verbose'] or []
2630 keep = ui.verbose and ['verbose'] or []
2631 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
2631 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
2632 ui.write("\n%s\n" % formatted)
2632 ui.write("\n%s\n" % formatted)
2633 if pruned:
2633 if pruned:
2634 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
2634 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
2635
2635
2636 if not ui.quiet:
2636 if not ui.quiet:
2637 # options
2637 # options
2638 if entry[1]:
2638 if entry[1]:
2639 option_lists.append((_("options:\n"), entry[1]))
2639 option_lists.append((_("options:\n"), entry[1]))
2640
2640
2641 addglobalopts(False)
2641 addglobalopts(False)
2642
2642
2643 # check if this command shadows a non-trivial (multi-line)
2643 # check if this command shadows a non-trivial (multi-line)
2644 # extension help text
2644 # extension help text
2645 try:
2645 try:
2646 mod = extensions.find(name)
2646 mod = extensions.find(name)
2647 doc = gettext(mod.__doc__) or ''
2647 doc = gettext(mod.__doc__) or ''
2648 if '\n' in doc.strip():
2648 if '\n' in doc.strip():
2649 msg = _('use "hg help -e %s" to show help for '
2649 msg = _('use "hg help -e %s" to show help for '
2650 'the %s extension') % (name, name)
2650 'the %s extension') % (name, name)
2651 ui.write('\n%s\n' % msg)
2651 ui.write('\n%s\n' % msg)
2652 except KeyError:
2652 except KeyError:
2653 pass
2653 pass
2654
2654
2655 def helplist(header, select=None):
2655 def helplist(header, select=None):
2656 h = {}
2656 h = {}
2657 cmds = {}
2657 cmds = {}
2658 for c, e in table.iteritems():
2658 for c, e in table.iteritems():
2659 f = c.split("|", 1)[0]
2659 f = c.split("|", 1)[0]
2660 if select and not select(f):
2660 if select and not select(f):
2661 continue
2661 continue
2662 if (not select and name != 'shortlist' and
2662 if (not select and name != 'shortlist' and
2663 e[0].__module__ != __name__):
2663 e[0].__module__ != __name__):
2664 continue
2664 continue
2665 if name == "shortlist" and not f.startswith("^"):
2665 if name == "shortlist" and not f.startswith("^"):
2666 continue
2666 continue
2667 f = f.lstrip("^")
2667 f = f.lstrip("^")
2668 if not ui.debugflag and f.startswith("debug"):
2668 if not ui.debugflag and f.startswith("debug"):
2669 continue
2669 continue
2670 doc = e[0].__doc__
2670 doc = e[0].__doc__
2671 if doc and 'DEPRECATED' in doc and not ui.verbose:
2671 if doc and 'DEPRECATED' in doc and not ui.verbose:
2672 continue
2672 continue
2673 doc = gettext(doc)
2673 doc = gettext(doc)
2674 if not doc:
2674 if not doc:
2675 doc = _("(no help text available)")
2675 doc = _("(no help text available)")
2676 h[f] = doc.splitlines()[0].rstrip()
2676 h[f] = doc.splitlines()[0].rstrip()
2677 cmds[f] = c.lstrip("^")
2677 cmds[f] = c.lstrip("^")
2678
2678
2679 if not h:
2679 if not h:
2680 ui.status(_('no commands defined\n'))
2680 ui.status(_('no commands defined\n'))
2681 return
2681 return
2682
2682
2683 ui.status(header)
2683 ui.status(header)
2684 fns = sorted(h)
2684 fns = sorted(h)
2685 m = max(map(len, fns))
2685 m = max(map(len, fns))
2686 for f in fns:
2686 for f in fns:
2687 if ui.verbose:
2687 if ui.verbose:
2688 commands = cmds[f].replace("|",", ")
2688 commands = cmds[f].replace("|",", ")
2689 ui.write(" %s:\n %s\n"%(commands, h[f]))
2689 ui.write(" %s:\n %s\n"%(commands, h[f]))
2690 else:
2690 else:
2691 ui.write('%s\n' % (util.wrap(h[f], textwidth,
2691 ui.write('%s\n' % (util.wrap(h[f], textwidth,
2692 initindent=' %-*s ' % (m, f),
2692 initindent=' %-*s ' % (m, f),
2693 hangindent=' ' * (m + 4))))
2693 hangindent=' ' * (m + 4))))
2694
2694
2695 if not ui.quiet:
2695 if not ui.quiet:
2696 addglobalopts(True)
2696 addglobalopts(True)
2697
2697
2698 def helptopic(name):
2698 def helptopic(name):
2699 for names, header, doc in help.helptable:
2699 for names, header, doc in help.helptable:
2700 if name in names:
2700 if name in names:
2701 break
2701 break
2702 else:
2702 else:
2703 raise error.UnknownCommand(name)
2703 raise error.UnknownCommand(name)
2704
2704
2705 # description
2705 # description
2706 if not doc:
2706 if not doc:
2707 doc = _("(no help text available)")
2707 doc = _("(no help text available)")
2708 if hasattr(doc, '__call__'):
2708 if hasattr(doc, '__call__'):
2709 doc = doc()
2709 doc = doc()
2710
2710
2711 ui.write("%s\n\n" % header)
2711 ui.write("%s\n\n" % header)
2712 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2712 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2713 try:
2713 try:
2714 cmdutil.findcmd(name, table)
2714 cmdutil.findcmd(name, table)
2715 ui.write(_('\nuse "hg help -c %s" to see help for '
2715 ui.write(_('\nuse "hg help -c %s" to see help for '
2716 'the %s command\n') % (name, name))
2716 'the %s command\n') % (name, name))
2717 except error.UnknownCommand:
2717 except error.UnknownCommand:
2718 pass
2718 pass
2719
2719
2720 def helpext(name):
2720 def helpext(name):
2721 try:
2721 try:
2722 mod = extensions.find(name)
2722 mod = extensions.find(name)
2723 doc = gettext(mod.__doc__) or _('no help text available')
2723 doc = gettext(mod.__doc__) or _('no help text available')
2724 except KeyError:
2724 except KeyError:
2725 mod = None
2725 mod = None
2726 doc = extensions.disabledext(name)
2726 doc = extensions.disabledext(name)
2727 if not doc:
2727 if not doc:
2728 raise error.UnknownCommand(name)
2728 raise error.UnknownCommand(name)
2729
2729
2730 if '\n' not in doc:
2730 if '\n' not in doc:
2731 head, tail = doc, ""
2731 head, tail = doc, ""
2732 else:
2732 else:
2733 head, tail = doc.split('\n', 1)
2733 head, tail = doc.split('\n', 1)
2734 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2734 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2735 if tail:
2735 if tail:
2736 ui.write(minirst.format(tail, textwidth))
2736 ui.write(minirst.format(tail, textwidth))
2737 ui.status('\n\n')
2737 ui.status('\n\n')
2738
2738
2739 if mod:
2739 if mod:
2740 try:
2740 try:
2741 ct = mod.cmdtable
2741 ct = mod.cmdtable
2742 except AttributeError:
2742 except AttributeError:
2743 ct = {}
2743 ct = {}
2744 modcmds = set([c.split('|', 1)[0] for c in ct])
2744 modcmds = set([c.split('|', 1)[0] for c in ct])
2745 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2745 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2746 else:
2746 else:
2747 ui.write(_('use "hg help extensions" for information on enabling '
2747 ui.write(_('use "hg help extensions" for information on enabling '
2748 'extensions\n'))
2748 'extensions\n'))
2749
2749
2750 def helpextcmd(name):
2750 def helpextcmd(name):
2751 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2751 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2752 doc = gettext(mod.__doc__).splitlines()[0]
2752 doc = gettext(mod.__doc__).splitlines()[0]
2753
2753
2754 msg = help.listexts(_("'%s' is provided by the following "
2754 msg = help.listexts(_("'%s' is provided by the following "
2755 "extension:") % cmd, {ext: doc}, indent=4)
2755 "extension:") % cmd, {ext: doc}, indent=4)
2756 ui.write(minirst.format(msg, textwidth))
2756 ui.write(minirst.format(msg, textwidth))
2757 ui.write('\n\n')
2757 ui.write('\n\n')
2758 ui.write(_('use "hg help extensions" for information on enabling '
2758 ui.write(_('use "hg help extensions" for information on enabling '
2759 'extensions\n'))
2759 'extensions\n'))
2760
2760
2761 help.addtopichook('revsets', revset.makedoc)
2762 help.addtopichook('templates', templatekw.makedoc)
2763 help.addtopichook('templates', templatefilters.makedoc)
2764
2765 if name and name != 'shortlist':
2761 if name and name != 'shortlist':
2766 i = None
2762 i = None
2767 if unknowncmd:
2763 if unknowncmd:
2768 queries = (helpextcmd,)
2764 queries = (helpextcmd,)
2769 elif opts.get('extension'):
2765 elif opts.get('extension'):
2770 queries = (helpext,)
2766 queries = (helpext,)
2771 elif opts.get('command'):
2767 elif opts.get('command'):
2772 queries = (helpcmd,)
2768 queries = (helpcmd,)
2773 else:
2769 else:
2774 queries = (helptopic, helpcmd, helpext, helpextcmd)
2770 queries = (helptopic, helpcmd, helpext, helpextcmd)
2775 for f in queries:
2771 for f in queries:
2776 try:
2772 try:
2777 f(name)
2773 f(name)
2778 i = None
2774 i = None
2779 break
2775 break
2780 except error.UnknownCommand, inst:
2776 except error.UnknownCommand, inst:
2781 i = inst
2777 i = inst
2782 if i:
2778 if i:
2783 raise i
2779 raise i
2784
2780
2785 else:
2781 else:
2786 # program name
2782 # program name
2787 if ui.verbose or with_version:
2783 if ui.verbose or with_version:
2788 version_(ui)
2784 version_(ui)
2789 else:
2785 else:
2790 ui.status(_("Mercurial Distributed SCM\n"))
2786 ui.status(_("Mercurial Distributed SCM\n"))
2791 ui.status('\n')
2787 ui.status('\n')
2792
2788
2793 # list of commands
2789 # list of commands
2794 if name == "shortlist":
2790 if name == "shortlist":
2795 header = _('basic commands:\n\n')
2791 header = _('basic commands:\n\n')
2796 else:
2792 else:
2797 header = _('list of commands:\n\n')
2793 header = _('list of commands:\n\n')
2798
2794
2799 helplist(header)
2795 helplist(header)
2800 if name != 'shortlist':
2796 if name != 'shortlist':
2801 text = help.listexts(_('enabled extensions:'), extensions.enabled())
2797 text = help.listexts(_('enabled extensions:'), extensions.enabled())
2802 if text:
2798 if text:
2803 ui.write("\n%s\n" % minirst.format(text, textwidth))
2799 ui.write("\n%s\n" % minirst.format(text, textwidth))
2804
2800
2805 # list all option lists
2801 # list all option lists
2806 opt_output = []
2802 opt_output = []
2807 multioccur = False
2803 multioccur = False
2808 for title, options in option_lists:
2804 for title, options in option_lists:
2809 opt_output.append(("\n%s" % title, None))
2805 opt_output.append(("\n%s" % title, None))
2810 for option in options:
2806 for option in options:
2811 if len(option) == 5:
2807 if len(option) == 5:
2812 shortopt, longopt, default, desc, optlabel = option
2808 shortopt, longopt, default, desc, optlabel = option
2813 else:
2809 else:
2814 shortopt, longopt, default, desc = option
2810 shortopt, longopt, default, desc = option
2815 optlabel = _("VALUE") # default label
2811 optlabel = _("VALUE") # default label
2816
2812
2817 if _("DEPRECATED") in desc and not ui.verbose:
2813 if _("DEPRECATED") in desc and not ui.verbose:
2818 continue
2814 continue
2819 if isinstance(default, list):
2815 if isinstance(default, list):
2820 numqualifier = " %s [+]" % optlabel
2816 numqualifier = " %s [+]" % optlabel
2821 multioccur = True
2817 multioccur = True
2822 elif (default is not None) and not isinstance(default, bool):
2818 elif (default is not None) and not isinstance(default, bool):
2823 numqualifier = " %s" % optlabel
2819 numqualifier = " %s" % optlabel
2824 else:
2820 else:
2825 numqualifier = ""
2821 numqualifier = ""
2826 opt_output.append(("%2s%s" %
2822 opt_output.append(("%2s%s" %
2827 (shortopt and "-%s" % shortopt,
2823 (shortopt and "-%s" % shortopt,
2828 longopt and " --%s%s" %
2824 longopt and " --%s%s" %
2829 (longopt, numqualifier)),
2825 (longopt, numqualifier)),
2830 "%s%s" % (desc,
2826 "%s%s" % (desc,
2831 default
2827 default
2832 and _(" (default: %s)") % default
2828 and _(" (default: %s)") % default
2833 or "")))
2829 or "")))
2834 if multioccur:
2830 if multioccur:
2835 msg = _("\n[+] marked option can be specified multiple times")
2831 msg = _("\n[+] marked option can be specified multiple times")
2836 if ui.verbose and name != 'shortlist':
2832 if ui.verbose and name != 'shortlist':
2837 opt_output.append((msg, None))
2833 opt_output.append((msg, None))
2838 else:
2834 else:
2839 opt_output.insert(-1, (msg, None))
2835 opt_output.insert(-1, (msg, None))
2840
2836
2841 if not name:
2837 if not name:
2842 ui.write(_("\nadditional help topics:\n\n"))
2838 ui.write(_("\nadditional help topics:\n\n"))
2843 topics = []
2839 topics = []
2844 for names, header, doc in help.helptable:
2840 for names, header, doc in help.helptable:
2845 topics.append((sorted(names, key=len, reverse=True)[0], header))
2841 topics.append((sorted(names, key=len, reverse=True)[0], header))
2846 topics_len = max([len(s[0]) for s in topics])
2842 topics_len = max([len(s[0]) for s in topics])
2847 for t, desc in topics:
2843 for t, desc in topics:
2848 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2844 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2849
2845
2850 if opt_output:
2846 if opt_output:
2851 colwidth = encoding.colwidth
2847 colwidth = encoding.colwidth
2852 # normalize: (opt or message, desc or None, width of opt)
2848 # normalize: (opt or message, desc or None, width of opt)
2853 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2849 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2854 for opt, desc in opt_output]
2850 for opt, desc in opt_output]
2855 hanging = max([e[2] for e in entries])
2851 hanging = max([e[2] for e in entries])
2856 for opt, desc, width in entries:
2852 for opt, desc, width in entries:
2857 if desc:
2853 if desc:
2858 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2854 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2859 hangindent = ' ' * (hanging + 3)
2855 hangindent = ' ' * (hanging + 3)
2860 ui.write('%s\n' % (util.wrap(desc, textwidth,
2856 ui.write('%s\n' % (util.wrap(desc, textwidth,
2861 initindent=initindent,
2857 initindent=initindent,
2862 hangindent=hangindent)))
2858 hangindent=hangindent)))
2863 else:
2859 else:
2864 ui.write("%s\n" % opt)
2860 ui.write("%s\n" % opt)
2865
2861
2866 @command('identify|id',
2862 @command('identify|id',
2867 [('r', 'rev', '',
2863 [('r', 'rev', '',
2868 _('identify the specified revision'), _('REV')),
2864 _('identify the specified revision'), _('REV')),
2869 ('n', 'num', None, _('show local revision number')),
2865 ('n', 'num', None, _('show local revision number')),
2870 ('i', 'id', None, _('show global revision id')),
2866 ('i', 'id', None, _('show global revision id')),
2871 ('b', 'branch', None, _('show branch')),
2867 ('b', 'branch', None, _('show branch')),
2872 ('t', 'tags', None, _('show tags')),
2868 ('t', 'tags', None, _('show tags')),
2873 ('B', 'bookmarks', None, _('show bookmarks'))],
2869 ('B', 'bookmarks', None, _('show bookmarks'))],
2874 _('[-nibtB] [-r REV] [SOURCE]'))
2870 _('[-nibtB] [-r REV] [SOURCE]'))
2875 def identify(ui, repo, source=None, rev=None,
2871 def identify(ui, repo, source=None, rev=None,
2876 num=None, id=None, branch=None, tags=None, bookmarks=None):
2872 num=None, id=None, branch=None, tags=None, bookmarks=None):
2877 """identify the working copy or specified revision
2873 """identify the working copy or specified revision
2878
2874
2879 Print a summary identifying the repository state at REV using one or
2875 Print a summary identifying the repository state at REV using one or
2880 two parent hash identifiers, followed by a "+" if the working
2876 two parent hash identifiers, followed by a "+" if the working
2881 directory has uncommitted changes, the branch name (if not default),
2877 directory has uncommitted changes, the branch name (if not default),
2882 a list of tags, and a list of bookmarks.
2878 a list of tags, and a list of bookmarks.
2883
2879
2884 When REV is not given, print a summary of the current state of the
2880 When REV is not given, print a summary of the current state of the
2885 repository.
2881 repository.
2886
2882
2887 Specifying a path to a repository root or Mercurial bundle will
2883 Specifying a path to a repository root or Mercurial bundle will
2888 cause lookup to operate on that repository/bundle.
2884 cause lookup to operate on that repository/bundle.
2889
2885
2890 Returns 0 if successful.
2886 Returns 0 if successful.
2891 """
2887 """
2892
2888
2893 if not repo and not source:
2889 if not repo and not source:
2894 raise util.Abort(_("there is no Mercurial repository here "
2890 raise util.Abort(_("there is no Mercurial repository here "
2895 "(.hg not found)"))
2891 "(.hg not found)"))
2896
2892
2897 hexfunc = ui.debugflag and hex or short
2893 hexfunc = ui.debugflag and hex or short
2898 default = not (num or id or branch or tags or bookmarks)
2894 default = not (num or id or branch or tags or bookmarks)
2899 output = []
2895 output = []
2900 revs = []
2896 revs = []
2901
2897
2902 if source:
2898 if source:
2903 source, branches = hg.parseurl(ui.expandpath(source))
2899 source, branches = hg.parseurl(ui.expandpath(source))
2904 repo = hg.repository(ui, source)
2900 repo = hg.repository(ui, source)
2905 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2901 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2906
2902
2907 if not repo.local():
2903 if not repo.local():
2908 if num or branch or tags:
2904 if num or branch or tags:
2909 raise util.Abort(
2905 raise util.Abort(
2910 _("can't query remote revision number, branch, or tags"))
2906 _("can't query remote revision number, branch, or tags"))
2911 if not rev and revs:
2907 if not rev and revs:
2912 rev = revs[0]
2908 rev = revs[0]
2913 if not rev:
2909 if not rev:
2914 rev = "tip"
2910 rev = "tip"
2915
2911
2916 remoterev = repo.lookup(rev)
2912 remoterev = repo.lookup(rev)
2917 if default or id:
2913 if default or id:
2918 output = [hexfunc(remoterev)]
2914 output = [hexfunc(remoterev)]
2919
2915
2920 def getbms():
2916 def getbms():
2921 bms = []
2917 bms = []
2922
2918
2923 if 'bookmarks' in repo.listkeys('namespaces'):
2919 if 'bookmarks' in repo.listkeys('namespaces'):
2924 hexremoterev = hex(remoterev)
2920 hexremoterev = hex(remoterev)
2925 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
2921 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
2926 if bmr == hexremoterev]
2922 if bmr == hexremoterev]
2927
2923
2928 return bms
2924 return bms
2929
2925
2930 if bookmarks:
2926 if bookmarks:
2931 output.extend(getbms())
2927 output.extend(getbms())
2932 elif default and not ui.quiet:
2928 elif default and not ui.quiet:
2933 # multiple bookmarks for a single parent separated by '/'
2929 # multiple bookmarks for a single parent separated by '/'
2934 bm = '/'.join(getbms())
2930 bm = '/'.join(getbms())
2935 if bm:
2931 if bm:
2936 output.append(bm)
2932 output.append(bm)
2937 else:
2933 else:
2938 if not rev:
2934 if not rev:
2939 ctx = repo[None]
2935 ctx = repo[None]
2940 parents = ctx.parents()
2936 parents = ctx.parents()
2941 changed = ""
2937 changed = ""
2942 if default or id or num:
2938 if default or id or num:
2943 changed = util.any(repo.status()) and "+" or ""
2939 changed = util.any(repo.status()) and "+" or ""
2944 if default or id:
2940 if default or id:
2945 output = ["%s%s" %
2941 output = ["%s%s" %
2946 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2942 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2947 if num:
2943 if num:
2948 output.append("%s%s" %
2944 output.append("%s%s" %
2949 ('+'.join([str(p.rev()) for p in parents]), changed))
2945 ('+'.join([str(p.rev()) for p in parents]), changed))
2950 else:
2946 else:
2951 ctx = cmdutil.revsingle(repo, rev)
2947 ctx = cmdutil.revsingle(repo, rev)
2952 if default or id:
2948 if default or id:
2953 output = [hexfunc(ctx.node())]
2949 output = [hexfunc(ctx.node())]
2954 if num:
2950 if num:
2955 output.append(str(ctx.rev()))
2951 output.append(str(ctx.rev()))
2956
2952
2957 if default and not ui.quiet:
2953 if default and not ui.quiet:
2958 b = ctx.branch()
2954 b = ctx.branch()
2959 if b != 'default':
2955 if b != 'default':
2960 output.append("(%s)" % b)
2956 output.append("(%s)" % b)
2961
2957
2962 # multiple tags for a single parent separated by '/'
2958 # multiple tags for a single parent separated by '/'
2963 t = '/'.join(ctx.tags())
2959 t = '/'.join(ctx.tags())
2964 if t:
2960 if t:
2965 output.append(t)
2961 output.append(t)
2966
2962
2967 # multiple bookmarks for a single parent separated by '/'
2963 # multiple bookmarks for a single parent separated by '/'
2968 bm = '/'.join(ctx.bookmarks())
2964 bm = '/'.join(ctx.bookmarks())
2969 if bm:
2965 if bm:
2970 output.append(bm)
2966 output.append(bm)
2971 else:
2967 else:
2972 if branch:
2968 if branch:
2973 output.append(ctx.branch())
2969 output.append(ctx.branch())
2974
2970
2975 if tags:
2971 if tags:
2976 output.extend(ctx.tags())
2972 output.extend(ctx.tags())
2977
2973
2978 if bookmarks:
2974 if bookmarks:
2979 output.extend(ctx.bookmarks())
2975 output.extend(ctx.bookmarks())
2980
2976
2981 ui.write("%s\n" % ' '.join(output))
2977 ui.write("%s\n" % ' '.join(output))
2982
2978
2983 @command('import|patch',
2979 @command('import|patch',
2984 [('p', 'strip', 1,
2980 [('p', 'strip', 1,
2985 _('directory strip option for patch. This has the same '
2981 _('directory strip option for patch. This has the same '
2986 'meaning as the corresponding patch option'), _('NUM')),
2982 'meaning as the corresponding patch option'), _('NUM')),
2987 ('b', 'base', '', _('base path'), _('PATH')),
2983 ('b', 'base', '', _('base path'), _('PATH')),
2988 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
2984 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
2989 ('', 'no-commit', None,
2985 ('', 'no-commit', None,
2990 _("don't commit, just update the working directory")),
2986 _("don't commit, just update the working directory")),
2991 ('', 'exact', None,
2987 ('', 'exact', None,
2992 _('apply patch to the nodes from which it was generated')),
2988 _('apply patch to the nodes from which it was generated')),
2993 ('', 'import-branch', None,
2989 ('', 'import-branch', None,
2994 _('use any branch information in patch (implied by --exact)'))] +
2990 _('use any branch information in patch (implied by --exact)'))] +
2995 commitopts + commitopts2 + similarityopts,
2991 commitopts + commitopts2 + similarityopts,
2996 _('[OPTION]... PATCH...'))
2992 _('[OPTION]... PATCH...'))
2997 def import_(ui, repo, patch1, *patches, **opts):
2993 def import_(ui, repo, patch1, *patches, **opts):
2998 """import an ordered set of patches
2994 """import an ordered set of patches
2999
2995
3000 Import a list of patches and commit them individually (unless
2996 Import a list of patches and commit them individually (unless
3001 --no-commit is specified).
2997 --no-commit is specified).
3002
2998
3003 If there are outstanding changes in the working directory, import
2999 If there are outstanding changes in the working directory, import
3004 will abort unless given the -f/--force flag.
3000 will abort unless given the -f/--force flag.
3005
3001
3006 You can import a patch straight from a mail message. Even patches
3002 You can import a patch straight from a mail message. Even patches
3007 as attachments work (to use the body part, it must have type
3003 as attachments work (to use the body part, it must have type
3008 text/plain or text/x-patch). From and Subject headers of email
3004 text/plain or text/x-patch). From and Subject headers of email
3009 message are used as default committer and commit message. All
3005 message are used as default committer and commit message. All
3010 text/plain body parts before first diff are added to commit
3006 text/plain body parts before first diff are added to commit
3011 message.
3007 message.
3012
3008
3013 If the imported patch was generated by :hg:`export`, user and
3009 If the imported patch was generated by :hg:`export`, user and
3014 description from patch override values from message headers and
3010 description from patch override values from message headers and
3015 body. Values given on command line with -m/--message and -u/--user
3011 body. Values given on command line with -m/--message and -u/--user
3016 override these.
3012 override these.
3017
3013
3018 If --exact is specified, import will set the working directory to
3014 If --exact is specified, import will set the working directory to
3019 the parent of each patch before applying it, and will abort if the
3015 the parent of each patch before applying it, and will abort if the
3020 resulting changeset has a different ID than the one recorded in
3016 resulting changeset has a different ID than the one recorded in
3021 the patch. This may happen due to character set problems or other
3017 the patch. This may happen due to character set problems or other
3022 deficiencies in the text patch format.
3018 deficiencies in the text patch format.
3023
3019
3024 With -s/--similarity, hg will attempt to discover renames and
3020 With -s/--similarity, hg will attempt to discover renames and
3025 copies in the patch in the same way as 'addremove'.
3021 copies in the patch in the same way as 'addremove'.
3026
3022
3027 To read a patch from standard input, use "-" as the patch name. If
3023 To read a patch from standard input, use "-" as the patch name. If
3028 a URL is specified, the patch will be downloaded from it.
3024 a URL is specified, the patch will be downloaded from it.
3029 See :hg:`help dates` for a list of formats valid for -d/--date.
3025 See :hg:`help dates` for a list of formats valid for -d/--date.
3030
3026
3031 Returns 0 on success.
3027 Returns 0 on success.
3032 """
3028 """
3033 patches = (patch1,) + patches
3029 patches = (patch1,) + patches
3034
3030
3035 date = opts.get('date')
3031 date = opts.get('date')
3036 if date:
3032 if date:
3037 opts['date'] = util.parsedate(date)
3033 opts['date'] = util.parsedate(date)
3038
3034
3039 try:
3035 try:
3040 sim = float(opts.get('similarity') or 0)
3036 sim = float(opts.get('similarity') or 0)
3041 except ValueError:
3037 except ValueError:
3042 raise util.Abort(_('similarity must be a number'))
3038 raise util.Abort(_('similarity must be a number'))
3043 if sim < 0 or sim > 100:
3039 if sim < 0 or sim > 100:
3044 raise util.Abort(_('similarity must be between 0 and 100'))
3040 raise util.Abort(_('similarity must be between 0 and 100'))
3045
3041
3046 if opts.get('exact') or not opts.get('force'):
3042 if opts.get('exact') or not opts.get('force'):
3047 cmdutil.bailifchanged(repo)
3043 cmdutil.bailifchanged(repo)
3048
3044
3049 d = opts["base"]
3045 d = opts["base"]
3050 strip = opts["strip"]
3046 strip = opts["strip"]
3051 wlock = lock = None
3047 wlock = lock = None
3052 msgs = []
3048 msgs = []
3053
3049
3054 def tryone(ui, hunk):
3050 def tryone(ui, hunk):
3055 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3051 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3056 patch.extract(ui, hunk)
3052 patch.extract(ui, hunk)
3057
3053
3058 if not tmpname:
3054 if not tmpname:
3059 return None
3055 return None
3060 commitid = _('to working directory')
3056 commitid = _('to working directory')
3061
3057
3062 try:
3058 try:
3063 cmdline_message = cmdutil.logmessage(opts)
3059 cmdline_message = cmdutil.logmessage(opts)
3064 if cmdline_message:
3060 if cmdline_message:
3065 # pickup the cmdline msg
3061 # pickup the cmdline msg
3066 message = cmdline_message
3062 message = cmdline_message
3067 elif message:
3063 elif message:
3068 # pickup the patch msg
3064 # pickup the patch msg
3069 message = message.strip()
3065 message = message.strip()
3070 else:
3066 else:
3071 # launch the editor
3067 # launch the editor
3072 message = None
3068 message = None
3073 ui.debug('message:\n%s\n' % message)
3069 ui.debug('message:\n%s\n' % message)
3074
3070
3075 wp = repo.parents()
3071 wp = repo.parents()
3076 if opts.get('exact'):
3072 if opts.get('exact'):
3077 if not nodeid or not p1:
3073 if not nodeid or not p1:
3078 raise util.Abort(_('not a Mercurial patch'))
3074 raise util.Abort(_('not a Mercurial patch'))
3079 p1 = repo.lookup(p1)
3075 p1 = repo.lookup(p1)
3080 p2 = repo.lookup(p2 or hex(nullid))
3076 p2 = repo.lookup(p2 or hex(nullid))
3081
3077
3082 if p1 != wp[0].node():
3078 if p1 != wp[0].node():
3083 hg.clean(repo, p1)
3079 hg.clean(repo, p1)
3084 repo.dirstate.setparents(p1, p2)
3080 repo.dirstate.setparents(p1, p2)
3085 elif p2:
3081 elif p2:
3086 try:
3082 try:
3087 p1 = repo.lookup(p1)
3083 p1 = repo.lookup(p1)
3088 p2 = repo.lookup(p2)
3084 p2 = repo.lookup(p2)
3089 if p1 == wp[0].node():
3085 if p1 == wp[0].node():
3090 repo.dirstate.setparents(p1, p2)
3086 repo.dirstate.setparents(p1, p2)
3091 except error.RepoError:
3087 except error.RepoError:
3092 pass
3088 pass
3093 if opts.get('exact') or opts.get('import_branch'):
3089 if opts.get('exact') or opts.get('import_branch'):
3094 repo.dirstate.setbranch(branch or 'default')
3090 repo.dirstate.setbranch(branch or 'default')
3095
3091
3096 files = {}
3092 files = {}
3097 patch.patch(ui, repo, tmpname, strip=strip, cwd=repo.root,
3093 patch.patch(ui, repo, tmpname, strip=strip, cwd=repo.root,
3098 files=files, eolmode=None, similarity=sim / 100.0)
3094 files=files, eolmode=None, similarity=sim / 100.0)
3099 files = list(files)
3095 files = list(files)
3100 if opts.get('no_commit'):
3096 if opts.get('no_commit'):
3101 if message:
3097 if message:
3102 msgs.append(message)
3098 msgs.append(message)
3103 else:
3099 else:
3104 if opts.get('exact'):
3100 if opts.get('exact'):
3105 m = None
3101 m = None
3106 else:
3102 else:
3107 m = cmdutil.matchfiles(repo, files or [])
3103 m = cmdutil.matchfiles(repo, files or [])
3108 n = repo.commit(message, opts.get('user') or user,
3104 n = repo.commit(message, opts.get('user') or user,
3109 opts.get('date') or date, match=m,
3105 opts.get('date') or date, match=m,
3110 editor=cmdutil.commiteditor)
3106 editor=cmdutil.commiteditor)
3111 if opts.get('exact'):
3107 if opts.get('exact'):
3112 if hex(n) != nodeid:
3108 if hex(n) != nodeid:
3113 repo.rollback()
3109 repo.rollback()
3114 raise util.Abort(_('patch is damaged'
3110 raise util.Abort(_('patch is damaged'
3115 ' or loses information'))
3111 ' or loses information'))
3116 # Force a dirstate write so that the next transaction
3112 # Force a dirstate write so that the next transaction
3117 # backups an up-do-date file.
3113 # backups an up-do-date file.
3118 repo.dirstate.write()
3114 repo.dirstate.write()
3119 if n:
3115 if n:
3120 commitid = short(n)
3116 commitid = short(n)
3121
3117
3122 return commitid
3118 return commitid
3123 finally:
3119 finally:
3124 os.unlink(tmpname)
3120 os.unlink(tmpname)
3125
3121
3126 try:
3122 try:
3127 wlock = repo.wlock()
3123 wlock = repo.wlock()
3128 lock = repo.lock()
3124 lock = repo.lock()
3129 lastcommit = None
3125 lastcommit = None
3130 for p in patches:
3126 for p in patches:
3131 pf = os.path.join(d, p)
3127 pf = os.path.join(d, p)
3132
3128
3133 if pf == '-':
3129 if pf == '-':
3134 ui.status(_("applying patch from stdin\n"))
3130 ui.status(_("applying patch from stdin\n"))
3135 pf = sys.stdin
3131 pf = sys.stdin
3136 else:
3132 else:
3137 ui.status(_("applying %s\n") % p)
3133 ui.status(_("applying %s\n") % p)
3138 pf = url.open(ui, pf)
3134 pf = url.open(ui, pf)
3139
3135
3140 haspatch = False
3136 haspatch = False
3141 for hunk in patch.split(pf):
3137 for hunk in patch.split(pf):
3142 commitid = tryone(ui, hunk)
3138 commitid = tryone(ui, hunk)
3143 if commitid:
3139 if commitid:
3144 haspatch = True
3140 haspatch = True
3145 if lastcommit:
3141 if lastcommit:
3146 ui.status(_('applied %s\n') % lastcommit)
3142 ui.status(_('applied %s\n') % lastcommit)
3147 lastcommit = commitid
3143 lastcommit = commitid
3148
3144
3149 if not haspatch:
3145 if not haspatch:
3150 raise util.Abort(_('no diffs found'))
3146 raise util.Abort(_('no diffs found'))
3151
3147
3152 if msgs:
3148 if msgs:
3153 repo.opener.write('last-message.txt', '\n* * *\n'.join(msgs))
3149 repo.opener.write('last-message.txt', '\n* * *\n'.join(msgs))
3154 finally:
3150 finally:
3155 release(lock, wlock)
3151 release(lock, wlock)
3156
3152
3157 @command('incoming|in',
3153 @command('incoming|in',
3158 [('f', 'force', None,
3154 [('f', 'force', None,
3159 _('run even if remote repository is unrelated')),
3155 _('run even if remote repository is unrelated')),
3160 ('n', 'newest-first', None, _('show newest record first')),
3156 ('n', 'newest-first', None, _('show newest record first')),
3161 ('', 'bundle', '',
3157 ('', 'bundle', '',
3162 _('file to store the bundles into'), _('FILE')),
3158 _('file to store the bundles into'), _('FILE')),
3163 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3159 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3164 ('B', 'bookmarks', False, _("compare bookmarks")),
3160 ('B', 'bookmarks', False, _("compare bookmarks")),
3165 ('b', 'branch', [],
3161 ('b', 'branch', [],
3166 _('a specific branch you would like to pull'), _('BRANCH')),
3162 _('a specific branch you would like to pull'), _('BRANCH')),
3167 ] + logopts + remoteopts + subrepoopts,
3163 ] + logopts + remoteopts + subrepoopts,
3168 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3164 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3169 def incoming(ui, repo, source="default", **opts):
3165 def incoming(ui, repo, source="default", **opts):
3170 """show new changesets found in source
3166 """show new changesets found in source
3171
3167
3172 Show new changesets found in the specified path/URL or the default
3168 Show new changesets found in the specified path/URL or the default
3173 pull location. These are the changesets that would have been pulled
3169 pull location. These are the changesets that would have been pulled
3174 if a pull at the time you issued this command.
3170 if a pull at the time you issued this command.
3175
3171
3176 For remote repository, using --bundle avoids downloading the
3172 For remote repository, using --bundle avoids downloading the
3177 changesets twice if the incoming is followed by a pull.
3173 changesets twice if the incoming is followed by a pull.
3178
3174
3179 See pull for valid source format details.
3175 See pull for valid source format details.
3180
3176
3181 Returns 0 if there are incoming changes, 1 otherwise.
3177 Returns 0 if there are incoming changes, 1 otherwise.
3182 """
3178 """
3183 if opts.get('bundle') and opts.get('subrepos'):
3179 if opts.get('bundle') and opts.get('subrepos'):
3184 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3180 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3185
3181
3186 if opts.get('bookmarks'):
3182 if opts.get('bookmarks'):
3187 source, branches = hg.parseurl(ui.expandpath(source),
3183 source, branches = hg.parseurl(ui.expandpath(source),
3188 opts.get('branch'))
3184 opts.get('branch'))
3189 other = hg.repository(hg.remoteui(repo, opts), source)
3185 other = hg.repository(hg.remoteui(repo, opts), source)
3190 if 'bookmarks' not in other.listkeys('namespaces'):
3186 if 'bookmarks' not in other.listkeys('namespaces'):
3191 ui.warn(_("remote doesn't support bookmarks\n"))
3187 ui.warn(_("remote doesn't support bookmarks\n"))
3192 return 0
3188 return 0
3193 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3189 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3194 return bookmarks.diff(ui, repo, other)
3190 return bookmarks.diff(ui, repo, other)
3195
3191
3196 ret = hg.incoming(ui, repo, source, opts)
3192 ret = hg.incoming(ui, repo, source, opts)
3197 return ret
3193 return ret
3198
3194
3199 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3195 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3200 def init(ui, dest=".", **opts):
3196 def init(ui, dest=".", **opts):
3201 """create a new repository in the given directory
3197 """create a new repository in the given directory
3202
3198
3203 Initialize a new repository in the given directory. If the given
3199 Initialize a new repository in the given directory. If the given
3204 directory does not exist, it will be created.
3200 directory does not exist, it will be created.
3205
3201
3206 If no directory is given, the current directory is used.
3202 If no directory is given, the current directory is used.
3207
3203
3208 It is possible to specify an ``ssh://`` URL as the destination.
3204 It is possible to specify an ``ssh://`` URL as the destination.
3209 See :hg:`help urls` for more information.
3205 See :hg:`help urls` for more information.
3210
3206
3211 Returns 0 on success.
3207 Returns 0 on success.
3212 """
3208 """
3213 hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1)
3209 hg.repository(hg.remoteui(ui, opts), ui.expandpath(dest), create=1)
3214
3210
3215 @command('locate',
3211 @command('locate',
3216 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3212 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3217 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3213 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3218 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3214 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3219 ] + walkopts,
3215 ] + walkopts,
3220 _('[OPTION]... [PATTERN]...'))
3216 _('[OPTION]... [PATTERN]...'))
3221 def locate(ui, repo, *pats, **opts):
3217 def locate(ui, repo, *pats, **opts):
3222 """locate files matching specific patterns
3218 """locate files matching specific patterns
3223
3219
3224 Print files under Mercurial control in the working directory whose
3220 Print files under Mercurial control in the working directory whose
3225 names match the given patterns.
3221 names match the given patterns.
3226
3222
3227 By default, this command searches all directories in the working
3223 By default, this command searches all directories in the working
3228 directory. To search just the current directory and its
3224 directory. To search just the current directory and its
3229 subdirectories, use "--include .".
3225 subdirectories, use "--include .".
3230
3226
3231 If no patterns are given to match, this command prints the names
3227 If no patterns are given to match, this command prints the names
3232 of all files under Mercurial control in the working directory.
3228 of all files under Mercurial control in the working directory.
3233
3229
3234 If you want to feed the output of this command into the "xargs"
3230 If you want to feed the output of this command into the "xargs"
3235 command, use the -0 option to both this command and "xargs". This
3231 command, use the -0 option to both this command and "xargs". This
3236 will avoid the problem of "xargs" treating single filenames that
3232 will avoid the problem of "xargs" treating single filenames that
3237 contain whitespace as multiple filenames.
3233 contain whitespace as multiple filenames.
3238
3234
3239 Returns 0 if a match is found, 1 otherwise.
3235 Returns 0 if a match is found, 1 otherwise.
3240 """
3236 """
3241 end = opts.get('print0') and '\0' or '\n'
3237 end = opts.get('print0') and '\0' or '\n'
3242 rev = cmdutil.revsingle(repo, opts.get('rev'), None).node()
3238 rev = cmdutil.revsingle(repo, opts.get('rev'), None).node()
3243
3239
3244 ret = 1
3240 ret = 1
3245 m = cmdutil.match(repo, pats, opts, default='relglob')
3241 m = cmdutil.match(repo, pats, opts, default='relglob')
3246 m.bad = lambda x, y: False
3242 m.bad = lambda x, y: False
3247 for abs in repo[rev].walk(m):
3243 for abs in repo[rev].walk(m):
3248 if not rev and abs not in repo.dirstate:
3244 if not rev and abs not in repo.dirstate:
3249 continue
3245 continue
3250 if opts.get('fullpath'):
3246 if opts.get('fullpath'):
3251 ui.write(repo.wjoin(abs), end)
3247 ui.write(repo.wjoin(abs), end)
3252 else:
3248 else:
3253 ui.write(((pats and m.rel(abs)) or abs), end)
3249 ui.write(((pats and m.rel(abs)) or abs), end)
3254 ret = 0
3250 ret = 0
3255
3251
3256 return ret
3252 return ret
3257
3253
3258 @command('^log|history',
3254 @command('^log|history',
3259 [('f', 'follow', None,
3255 [('f', 'follow', None,
3260 _('follow changeset history, or file history across copies and renames')),
3256 _('follow changeset history, or file history across copies and renames')),
3261 ('', 'follow-first', None,
3257 ('', 'follow-first', None,
3262 _('only follow the first parent of merge changesets')),
3258 _('only follow the first parent of merge changesets')),
3263 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3259 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3264 ('C', 'copies', None, _('show copied files')),
3260 ('C', 'copies', None, _('show copied files')),
3265 ('k', 'keyword', [],
3261 ('k', 'keyword', [],
3266 _('do case-insensitive search for a given text'), _('TEXT')),
3262 _('do case-insensitive search for a given text'), _('TEXT')),
3267 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3263 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3268 ('', 'removed', None, _('include revisions where files were removed')),
3264 ('', 'removed', None, _('include revisions where files were removed')),
3269 ('m', 'only-merges', None, _('show only merges')),
3265 ('m', 'only-merges', None, _('show only merges')),
3270 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3266 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3271 ('', 'only-branch', [],
3267 ('', 'only-branch', [],
3272 _('show only changesets within the given named branch (DEPRECATED)'),
3268 _('show only changesets within the given named branch (DEPRECATED)'),
3273 _('BRANCH')),
3269 _('BRANCH')),
3274 ('b', 'branch', [],
3270 ('b', 'branch', [],
3275 _('show changesets within the given named branch'), _('BRANCH')),
3271 _('show changesets within the given named branch'), _('BRANCH')),
3276 ('P', 'prune', [],
3272 ('P', 'prune', [],
3277 _('do not display revision or any of its ancestors'), _('REV')),
3273 _('do not display revision or any of its ancestors'), _('REV')),
3278 ] + logopts + walkopts,
3274 ] + logopts + walkopts,
3279 _('[OPTION]... [FILE]'))
3275 _('[OPTION]... [FILE]'))
3280 def log(ui, repo, *pats, **opts):
3276 def log(ui, repo, *pats, **opts):
3281 """show revision history of entire repository or files
3277 """show revision history of entire repository or files
3282
3278
3283 Print the revision history of the specified files or the entire
3279 Print the revision history of the specified files or the entire
3284 project.
3280 project.
3285
3281
3286 File history is shown without following rename or copy history of
3282 File history is shown without following rename or copy history of
3287 files. Use -f/--follow with a filename to follow history across
3283 files. Use -f/--follow with a filename to follow history across
3288 renames and copies. --follow without a filename will only show
3284 renames and copies. --follow without a filename will only show
3289 ancestors or descendants of the starting revision. --follow-first
3285 ancestors or descendants of the starting revision. --follow-first
3290 only follows the first parent of merge revisions.
3286 only follows the first parent of merge revisions.
3291
3287
3292 If no revision range is specified, the default is ``tip:0`` unless
3288 If no revision range is specified, the default is ``tip:0`` unless
3293 --follow is set, in which case the working directory parent is
3289 --follow is set, in which case the working directory parent is
3294 used as the starting revision. You can specify a revision set for
3290 used as the starting revision. You can specify a revision set for
3295 log, see :hg:`help revsets` for more information.
3291 log, see :hg:`help revsets` for more information.
3296
3292
3297 See :hg:`help dates` for a list of formats valid for -d/--date.
3293 See :hg:`help dates` for a list of formats valid for -d/--date.
3298
3294
3299 By default this command prints revision number and changeset id,
3295 By default this command prints revision number and changeset id,
3300 tags, non-trivial parents, user, date and time, and a summary for
3296 tags, non-trivial parents, user, date and time, and a summary for
3301 each commit. When the -v/--verbose switch is used, the list of
3297 each commit. When the -v/--verbose switch is used, the list of
3302 changed files and full commit message are shown.
3298 changed files and full commit message are shown.
3303
3299
3304 .. note::
3300 .. note::
3305 log -p/--patch may generate unexpected diff output for merge
3301 log -p/--patch may generate unexpected diff output for merge
3306 changesets, as it will only compare the merge changeset against
3302 changesets, as it will only compare the merge changeset against
3307 its first parent. Also, only files different from BOTH parents
3303 its first parent. Also, only files different from BOTH parents
3308 will appear in files:.
3304 will appear in files:.
3309
3305
3310 Returns 0 on success.
3306 Returns 0 on success.
3311 """
3307 """
3312
3308
3313 matchfn = cmdutil.match(repo, pats, opts)
3309 matchfn = cmdutil.match(repo, pats, opts)
3314 limit = cmdutil.loglimit(opts)
3310 limit = cmdutil.loglimit(opts)
3315 count = 0
3311 count = 0
3316
3312
3317 endrev = None
3313 endrev = None
3318 if opts.get('copies') and opts.get('rev'):
3314 if opts.get('copies') and opts.get('rev'):
3319 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
3315 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
3320
3316
3321 df = False
3317 df = False
3322 if opts["date"]:
3318 if opts["date"]:
3323 df = util.matchdate(opts["date"])
3319 df = util.matchdate(opts["date"])
3324
3320
3325 branches = opts.get('branch', []) + opts.get('only_branch', [])
3321 branches = opts.get('branch', []) + opts.get('only_branch', [])
3326 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3322 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3327
3323
3328 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3324 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3329 def prep(ctx, fns):
3325 def prep(ctx, fns):
3330 rev = ctx.rev()
3326 rev = ctx.rev()
3331 parents = [p for p in repo.changelog.parentrevs(rev)
3327 parents = [p for p in repo.changelog.parentrevs(rev)
3332 if p != nullrev]
3328 if p != nullrev]
3333 if opts.get('no_merges') and len(parents) == 2:
3329 if opts.get('no_merges') and len(parents) == 2:
3334 return
3330 return
3335 if opts.get('only_merges') and len(parents) != 2:
3331 if opts.get('only_merges') and len(parents) != 2:
3336 return
3332 return
3337 if opts.get('branch') and ctx.branch() not in opts['branch']:
3333 if opts.get('branch') and ctx.branch() not in opts['branch']:
3338 return
3334 return
3339 if df and not df(ctx.date()[0]):
3335 if df and not df(ctx.date()[0]):
3340 return
3336 return
3341 if opts['user'] and not [k for k in opts['user']
3337 if opts['user'] and not [k for k in opts['user']
3342 if k.lower() in ctx.user().lower()]:
3338 if k.lower() in ctx.user().lower()]:
3343 return
3339 return
3344 if opts.get('keyword'):
3340 if opts.get('keyword'):
3345 for k in [kw.lower() for kw in opts['keyword']]:
3341 for k in [kw.lower() for kw in opts['keyword']]:
3346 if (k in ctx.user().lower() or
3342 if (k in ctx.user().lower() or
3347 k in ctx.description().lower() or
3343 k in ctx.description().lower() or
3348 k in " ".join(ctx.files()).lower()):
3344 k in " ".join(ctx.files()).lower()):
3349 break
3345 break
3350 else:
3346 else:
3351 return
3347 return
3352
3348
3353 copies = None
3349 copies = None
3354 if opts.get('copies') and rev:
3350 if opts.get('copies') and rev:
3355 copies = []
3351 copies = []
3356 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3352 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3357 for fn in ctx.files():
3353 for fn in ctx.files():
3358 rename = getrenamed(fn, rev)
3354 rename = getrenamed(fn, rev)
3359 if rename:
3355 if rename:
3360 copies.append((fn, rename[0]))
3356 copies.append((fn, rename[0]))
3361
3357
3362 revmatchfn = None
3358 revmatchfn = None
3363 if opts.get('patch') or opts.get('stat'):
3359 if opts.get('patch') or opts.get('stat'):
3364 if opts.get('follow') or opts.get('follow_first'):
3360 if opts.get('follow') or opts.get('follow_first'):
3365 # note: this might be wrong when following through merges
3361 # note: this might be wrong when following through merges
3366 revmatchfn = cmdutil.match(repo, fns, default='path')
3362 revmatchfn = cmdutil.match(repo, fns, default='path')
3367 else:
3363 else:
3368 revmatchfn = matchfn
3364 revmatchfn = matchfn
3369
3365
3370 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3366 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3371
3367
3372 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3368 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3373 if count == limit:
3369 if count == limit:
3374 break
3370 break
3375 if displayer.flush(ctx.rev()):
3371 if displayer.flush(ctx.rev()):
3376 count += 1
3372 count += 1
3377 displayer.close()
3373 displayer.close()
3378
3374
3379 @command('manifest',
3375 @command('manifest',
3380 [('r', 'rev', '', _('revision to display'), _('REV'))],
3376 [('r', 'rev', '', _('revision to display'), _('REV'))],
3381 _('[-r REV]'))
3377 _('[-r REV]'))
3382 def manifest(ui, repo, node=None, rev=None):
3378 def manifest(ui, repo, node=None, rev=None):
3383 """output the current or given revision of the project manifest
3379 """output the current or given revision of the project manifest
3384
3380
3385 Print a list of version controlled files for the given revision.
3381 Print a list of version controlled files for the given revision.
3386 If no revision is given, the first parent of the working directory
3382 If no revision is given, the first parent of the working directory
3387 is used, or the null revision if no revision is checked out.
3383 is used, or the null revision if no revision is checked out.
3388
3384
3389 With -v, print file permissions, symlink and executable bits.
3385 With -v, print file permissions, symlink and executable bits.
3390 With --debug, print file revision hashes.
3386 With --debug, print file revision hashes.
3391
3387
3392 Returns 0 on success.
3388 Returns 0 on success.
3393 """
3389 """
3394
3390
3395 if rev and node:
3391 if rev and node:
3396 raise util.Abort(_("please specify just one revision"))
3392 raise util.Abort(_("please specify just one revision"))
3397
3393
3398 if not node:
3394 if not node:
3399 node = rev
3395 node = rev
3400
3396
3401 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
3397 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
3402 ctx = cmdutil.revsingle(repo, node)
3398 ctx = cmdutil.revsingle(repo, node)
3403 for f in ctx:
3399 for f in ctx:
3404 if ui.debugflag:
3400 if ui.debugflag:
3405 ui.write("%40s " % hex(ctx.manifest()[f]))
3401 ui.write("%40s " % hex(ctx.manifest()[f]))
3406 if ui.verbose:
3402 if ui.verbose:
3407 ui.write(decor[ctx.flags(f)])
3403 ui.write(decor[ctx.flags(f)])
3408 ui.write("%s\n" % f)
3404 ui.write("%s\n" % f)
3409
3405
3410 @command('^merge',
3406 @command('^merge',
3411 [('f', 'force', None, _('force a merge with outstanding changes')),
3407 [('f', 'force', None, _('force a merge with outstanding changes')),
3412 ('t', 'tool', '', _('specify merge tool')),
3408 ('t', 'tool', '', _('specify merge tool')),
3413 ('r', 'rev', '', _('revision to merge'), _('REV')),
3409 ('r', 'rev', '', _('revision to merge'), _('REV')),
3414 ('P', 'preview', None,
3410 ('P', 'preview', None,
3415 _('review revisions to merge (no merge is performed)'))],
3411 _('review revisions to merge (no merge is performed)'))],
3416 _('[-P] [-f] [[-r] REV]'))
3412 _('[-P] [-f] [[-r] REV]'))
3417 def merge(ui, repo, node=None, **opts):
3413 def merge(ui, repo, node=None, **opts):
3418 """merge working directory with another revision
3414 """merge working directory with another revision
3419
3415
3420 The current working directory is updated with all changes made in
3416 The current working directory is updated with all changes made in
3421 the requested revision since the last common predecessor revision.
3417 the requested revision since the last common predecessor revision.
3422
3418
3423 Files that changed between either parent are marked as changed for
3419 Files that changed between either parent are marked as changed for
3424 the next commit and a commit must be performed before any further
3420 the next commit and a commit must be performed before any further
3425 updates to the repository are allowed. The next commit will have
3421 updates to the repository are allowed. The next commit will have
3426 two parents.
3422 two parents.
3427
3423
3428 ``--tool`` can be used to specify the merge tool used for file
3424 ``--tool`` can be used to specify the merge tool used for file
3429 merges. It overrides the HGMERGE environment variable and your
3425 merges. It overrides the HGMERGE environment variable and your
3430 configuration files. See :hg:`help merge-tools` for options.
3426 configuration files. See :hg:`help merge-tools` for options.
3431
3427
3432 If no revision is specified, the working directory's parent is a
3428 If no revision is specified, the working directory's parent is a
3433 head revision, and the current branch contains exactly one other
3429 head revision, and the current branch contains exactly one other
3434 head, the other head is merged with by default. Otherwise, an
3430 head, the other head is merged with by default. Otherwise, an
3435 explicit revision with which to merge with must be provided.
3431 explicit revision with which to merge with must be provided.
3436
3432
3437 :hg:`resolve` must be used to resolve unresolved files.
3433 :hg:`resolve` must be used to resolve unresolved files.
3438
3434
3439 To undo an uncommitted merge, use :hg:`update --clean .` which
3435 To undo an uncommitted merge, use :hg:`update --clean .` which
3440 will check out a clean copy of the original merge parent, losing
3436 will check out a clean copy of the original merge parent, losing
3441 all changes.
3437 all changes.
3442
3438
3443 Returns 0 on success, 1 if there are unresolved files.
3439 Returns 0 on success, 1 if there are unresolved files.
3444 """
3440 """
3445
3441
3446 if opts.get('rev') and node:
3442 if opts.get('rev') and node:
3447 raise util.Abort(_("please specify just one revision"))
3443 raise util.Abort(_("please specify just one revision"))
3448 if not node:
3444 if not node:
3449 node = opts.get('rev')
3445 node = opts.get('rev')
3450
3446
3451 if not node:
3447 if not node:
3452 branch = repo[None].branch()
3448 branch = repo[None].branch()
3453 bheads = repo.branchheads(branch)
3449 bheads = repo.branchheads(branch)
3454 if len(bheads) > 2:
3450 if len(bheads) > 2:
3455 raise util.Abort(_("branch '%s' has %d heads - "
3451 raise util.Abort(_("branch '%s' has %d heads - "
3456 "please merge with an explicit rev")
3452 "please merge with an explicit rev")
3457 % (branch, len(bheads)),
3453 % (branch, len(bheads)),
3458 hint=_("run 'hg heads .' to see heads"))
3454 hint=_("run 'hg heads .' to see heads"))
3459
3455
3460 parent = repo.dirstate.p1()
3456 parent = repo.dirstate.p1()
3461 if len(bheads) == 1:
3457 if len(bheads) == 1:
3462 if len(repo.heads()) > 1:
3458 if len(repo.heads()) > 1:
3463 raise util.Abort(_("branch '%s' has one head - "
3459 raise util.Abort(_("branch '%s' has one head - "
3464 "please merge with an explicit rev")
3460 "please merge with an explicit rev")
3465 % branch,
3461 % branch,
3466 hint=_("run 'hg heads' to see all heads"))
3462 hint=_("run 'hg heads' to see all heads"))
3467 msg = _('there is nothing to merge')
3463 msg = _('there is nothing to merge')
3468 if parent != repo.lookup(repo[None].branch()):
3464 if parent != repo.lookup(repo[None].branch()):
3469 msg = _('%s - use "hg update" instead') % msg
3465 msg = _('%s - use "hg update" instead') % msg
3470 raise util.Abort(msg)
3466 raise util.Abort(msg)
3471
3467
3472 if parent not in bheads:
3468 if parent not in bheads:
3473 raise util.Abort(_('working directory not at a head revision'),
3469 raise util.Abort(_('working directory not at a head revision'),
3474 hint=_("use 'hg update' or merge with an "
3470 hint=_("use 'hg update' or merge with an "
3475 "explicit revision"))
3471 "explicit revision"))
3476 node = parent == bheads[0] and bheads[-1] or bheads[0]
3472 node = parent == bheads[0] and bheads[-1] or bheads[0]
3477 else:
3473 else:
3478 node = cmdutil.revsingle(repo, node).node()
3474 node = cmdutil.revsingle(repo, node).node()
3479
3475
3480 if opts.get('preview'):
3476 if opts.get('preview'):
3481 # find nodes that are ancestors of p2 but not of p1
3477 # find nodes that are ancestors of p2 but not of p1
3482 p1 = repo.lookup('.')
3478 p1 = repo.lookup('.')
3483 p2 = repo.lookup(node)
3479 p2 = repo.lookup(node)
3484 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3480 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3485
3481
3486 displayer = cmdutil.show_changeset(ui, repo, opts)
3482 displayer = cmdutil.show_changeset(ui, repo, opts)
3487 for node in nodes:
3483 for node in nodes:
3488 displayer.show(repo[node])
3484 displayer.show(repo[node])
3489 displayer.close()
3485 displayer.close()
3490 return 0
3486 return 0
3491
3487
3492 try:
3488 try:
3493 # ui.forcemerge is an internal variable, do not document
3489 # ui.forcemerge is an internal variable, do not document
3494 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3490 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3495 return hg.merge(repo, node, force=opts.get('force'))
3491 return hg.merge(repo, node, force=opts.get('force'))
3496 finally:
3492 finally:
3497 ui.setconfig('ui', 'forcemerge', '')
3493 ui.setconfig('ui', 'forcemerge', '')
3498
3494
3499 @command('outgoing|out',
3495 @command('outgoing|out',
3500 [('f', 'force', None, _('run even when the destination is unrelated')),
3496 [('f', 'force', None, _('run even when the destination is unrelated')),
3501 ('r', 'rev', [],
3497 ('r', 'rev', [],
3502 _('a changeset intended to be included in the destination'), _('REV')),
3498 _('a changeset intended to be included in the destination'), _('REV')),
3503 ('n', 'newest-first', None, _('show newest record first')),
3499 ('n', 'newest-first', None, _('show newest record first')),
3504 ('B', 'bookmarks', False, _('compare bookmarks')),
3500 ('B', 'bookmarks', False, _('compare bookmarks')),
3505 ('b', 'branch', [], _('a specific branch you would like to push'),
3501 ('b', 'branch', [], _('a specific branch you would like to push'),
3506 _('BRANCH')),
3502 _('BRANCH')),
3507 ] + logopts + remoteopts + subrepoopts,
3503 ] + logopts + remoteopts + subrepoopts,
3508 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3504 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3509 def outgoing(ui, repo, dest=None, **opts):
3505 def outgoing(ui, repo, dest=None, **opts):
3510 """show changesets not found in the destination
3506 """show changesets not found in the destination
3511
3507
3512 Show changesets not found in the specified destination repository
3508 Show changesets not found in the specified destination repository
3513 or the default push location. These are the changesets that would
3509 or the default push location. These are the changesets that would
3514 be pushed if a push was requested.
3510 be pushed if a push was requested.
3515
3511
3516 See pull for details of valid destination formats.
3512 See pull for details of valid destination formats.
3517
3513
3518 Returns 0 if there are outgoing changes, 1 otherwise.
3514 Returns 0 if there are outgoing changes, 1 otherwise.
3519 """
3515 """
3520
3516
3521 if opts.get('bookmarks'):
3517 if opts.get('bookmarks'):
3522 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3518 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3523 dest, branches = hg.parseurl(dest, opts.get('branch'))
3519 dest, branches = hg.parseurl(dest, opts.get('branch'))
3524 other = hg.repository(hg.remoteui(repo, opts), dest)
3520 other = hg.repository(hg.remoteui(repo, opts), dest)
3525 if 'bookmarks' not in other.listkeys('namespaces'):
3521 if 'bookmarks' not in other.listkeys('namespaces'):
3526 ui.warn(_("remote doesn't support bookmarks\n"))
3522 ui.warn(_("remote doesn't support bookmarks\n"))
3527 return 0
3523 return 0
3528 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3524 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3529 return bookmarks.diff(ui, other, repo)
3525 return bookmarks.diff(ui, other, repo)
3530
3526
3531 ret = hg.outgoing(ui, repo, dest, opts)
3527 ret = hg.outgoing(ui, repo, dest, opts)
3532 return ret
3528 return ret
3533
3529
3534 @command('parents',
3530 @command('parents',
3535 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3531 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3536 ] + templateopts,
3532 ] + templateopts,
3537 _('[-r REV] [FILE]'))
3533 _('[-r REV] [FILE]'))
3538 def parents(ui, repo, file_=None, **opts):
3534 def parents(ui, repo, file_=None, **opts):
3539 """show the parents of the working directory or revision
3535 """show the parents of the working directory or revision
3540
3536
3541 Print the working directory's parent revisions. If a revision is
3537 Print the working directory's parent revisions. If a revision is
3542 given via -r/--rev, the parent of that revision will be printed.
3538 given via -r/--rev, the parent of that revision will be printed.
3543 If a file argument is given, the revision in which the file was
3539 If a file argument is given, the revision in which the file was
3544 last changed (before the working directory revision or the
3540 last changed (before the working directory revision or the
3545 argument to --rev if given) is printed.
3541 argument to --rev if given) is printed.
3546
3542
3547 Returns 0 on success.
3543 Returns 0 on success.
3548 """
3544 """
3549
3545
3550 ctx = cmdutil.revsingle(repo, opts.get('rev'), None)
3546 ctx = cmdutil.revsingle(repo, opts.get('rev'), None)
3551
3547
3552 if file_:
3548 if file_:
3553 m = cmdutil.match(repo, (file_,), opts)
3549 m = cmdutil.match(repo, (file_,), opts)
3554 if m.anypats() or len(m.files()) != 1:
3550 if m.anypats() or len(m.files()) != 1:
3555 raise util.Abort(_('can only specify an explicit filename'))
3551 raise util.Abort(_('can only specify an explicit filename'))
3556 file_ = m.files()[0]
3552 file_ = m.files()[0]
3557 filenodes = []
3553 filenodes = []
3558 for cp in ctx.parents():
3554 for cp in ctx.parents():
3559 if not cp:
3555 if not cp:
3560 continue
3556 continue
3561 try:
3557 try:
3562 filenodes.append(cp.filenode(file_))
3558 filenodes.append(cp.filenode(file_))
3563 except error.LookupError:
3559 except error.LookupError:
3564 pass
3560 pass
3565 if not filenodes:
3561 if not filenodes:
3566 raise util.Abort(_("'%s' not found in manifest!") % file_)
3562 raise util.Abort(_("'%s' not found in manifest!") % file_)
3567 fl = repo.file(file_)
3563 fl = repo.file(file_)
3568 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
3564 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
3569 else:
3565 else:
3570 p = [cp.node() for cp in ctx.parents()]
3566 p = [cp.node() for cp in ctx.parents()]
3571
3567
3572 displayer = cmdutil.show_changeset(ui, repo, opts)
3568 displayer = cmdutil.show_changeset(ui, repo, opts)
3573 for n in p:
3569 for n in p:
3574 if n != nullid:
3570 if n != nullid:
3575 displayer.show(repo[n])
3571 displayer.show(repo[n])
3576 displayer.close()
3572 displayer.close()
3577
3573
3578 @command('paths', [], _('[NAME]'))
3574 @command('paths', [], _('[NAME]'))
3579 def paths(ui, repo, search=None):
3575 def paths(ui, repo, search=None):
3580 """show aliases for remote repositories
3576 """show aliases for remote repositories
3581
3577
3582 Show definition of symbolic path name NAME. If no name is given,
3578 Show definition of symbolic path name NAME. If no name is given,
3583 show definition of all available names.
3579 show definition of all available names.
3584
3580
3585 Path names are defined in the [paths] section of your
3581 Path names are defined in the [paths] section of your
3586 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3582 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3587 repository, ``.hg/hgrc`` is used, too.
3583 repository, ``.hg/hgrc`` is used, too.
3588
3584
3589 The path names ``default`` and ``default-push`` have a special
3585 The path names ``default`` and ``default-push`` have a special
3590 meaning. When performing a push or pull operation, they are used
3586 meaning. When performing a push or pull operation, they are used
3591 as fallbacks if no location is specified on the command-line.
3587 as fallbacks if no location is specified on the command-line.
3592 When ``default-push`` is set, it will be used for push and
3588 When ``default-push`` is set, it will be used for push and
3593 ``default`` will be used for pull; otherwise ``default`` is used
3589 ``default`` will be used for pull; otherwise ``default`` is used
3594 as the fallback for both. When cloning a repository, the clone
3590 as the fallback for both. When cloning a repository, the clone
3595 source is written as ``default`` in ``.hg/hgrc``. Note that
3591 source is written as ``default`` in ``.hg/hgrc``. Note that
3596 ``default`` and ``default-push`` apply to all inbound (e.g.
3592 ``default`` and ``default-push`` apply to all inbound (e.g.
3597 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
3593 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
3598 :hg:`bundle`) operations.
3594 :hg:`bundle`) operations.
3599
3595
3600 See :hg:`help urls` for more information.
3596 See :hg:`help urls` for more information.
3601
3597
3602 Returns 0 on success.
3598 Returns 0 on success.
3603 """
3599 """
3604 if search:
3600 if search:
3605 for name, path in ui.configitems("paths"):
3601 for name, path in ui.configitems("paths"):
3606 if name == search:
3602 if name == search:
3607 ui.write("%s\n" % util.hidepassword(path))
3603 ui.write("%s\n" % util.hidepassword(path))
3608 return
3604 return
3609 ui.warn(_("not found!\n"))
3605 ui.warn(_("not found!\n"))
3610 return 1
3606 return 1
3611 else:
3607 else:
3612 for name, path in ui.configitems("paths"):
3608 for name, path in ui.configitems("paths"):
3613 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
3609 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
3614
3610
3615 def postincoming(ui, repo, modheads, optupdate, checkout):
3611 def postincoming(ui, repo, modheads, optupdate, checkout):
3616 if modheads == 0:
3612 if modheads == 0:
3617 return
3613 return
3618 if optupdate:
3614 if optupdate:
3619 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
3615 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
3620 return hg.update(repo, checkout)
3616 return hg.update(repo, checkout)
3621 else:
3617 else:
3622 ui.status(_("not updating, since new heads added\n"))
3618 ui.status(_("not updating, since new heads added\n"))
3623 if modheads > 1:
3619 if modheads > 1:
3624 currentbranchheads = len(repo.branchheads())
3620 currentbranchheads = len(repo.branchheads())
3625 if currentbranchheads == modheads:
3621 if currentbranchheads == modheads:
3626 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3622 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3627 elif currentbranchheads > 1:
3623 elif currentbranchheads > 1:
3628 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
3624 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
3629 else:
3625 else:
3630 ui.status(_("(run 'hg heads' to see heads)\n"))
3626 ui.status(_("(run 'hg heads' to see heads)\n"))
3631 else:
3627 else:
3632 ui.status(_("(run 'hg update' to get a working copy)\n"))
3628 ui.status(_("(run 'hg update' to get a working copy)\n"))
3633
3629
3634 @command('^pull',
3630 @command('^pull',
3635 [('u', 'update', None,
3631 [('u', 'update', None,
3636 _('update to new branch head if changesets were pulled')),
3632 _('update to new branch head if changesets were pulled')),
3637 ('f', 'force', None, _('run even when remote repository is unrelated')),
3633 ('f', 'force', None, _('run even when remote repository is unrelated')),
3638 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3634 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3639 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3635 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3640 ('b', 'branch', [], _('a specific branch you would like to pull'),
3636 ('b', 'branch', [], _('a specific branch you would like to pull'),
3641 _('BRANCH')),
3637 _('BRANCH')),
3642 ] + remoteopts,
3638 ] + remoteopts,
3643 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3639 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3644 def pull(ui, repo, source="default", **opts):
3640 def pull(ui, repo, source="default", **opts):
3645 """pull changes from the specified source
3641 """pull changes from the specified source
3646
3642
3647 Pull changes from a remote repository to a local one.
3643 Pull changes from a remote repository to a local one.
3648
3644
3649 This finds all changes from the repository at the specified path
3645 This finds all changes from the repository at the specified path
3650 or URL and adds them to a local repository (the current one unless
3646 or URL and adds them to a local repository (the current one unless
3651 -R is specified). By default, this does not update the copy of the
3647 -R is specified). By default, this does not update the copy of the
3652 project in the working directory.
3648 project in the working directory.
3653
3649
3654 Use :hg:`incoming` if you want to see what would have been added
3650 Use :hg:`incoming` if you want to see what would have been added
3655 by a pull at the time you issued this command. If you then decide
3651 by a pull at the time you issued this command. If you then decide
3656 to add those changes to the repository, you should use :hg:`pull
3652 to add those changes to the repository, you should use :hg:`pull
3657 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3653 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3658
3654
3659 If SOURCE is omitted, the 'default' path will be used.
3655 If SOURCE is omitted, the 'default' path will be used.
3660 See :hg:`help urls` for more information.
3656 See :hg:`help urls` for more information.
3661
3657
3662 Returns 0 on success, 1 if an update had unresolved files.
3658 Returns 0 on success, 1 if an update had unresolved files.
3663 """
3659 """
3664 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3660 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3665 other = hg.repository(hg.remoteui(repo, opts), source)
3661 other = hg.repository(hg.remoteui(repo, opts), source)
3666 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3662 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3667 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3663 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3668
3664
3669 if opts.get('bookmark'):
3665 if opts.get('bookmark'):
3670 if not revs:
3666 if not revs:
3671 revs = []
3667 revs = []
3672 rb = other.listkeys('bookmarks')
3668 rb = other.listkeys('bookmarks')
3673 for b in opts['bookmark']:
3669 for b in opts['bookmark']:
3674 if b not in rb:
3670 if b not in rb:
3675 raise util.Abort(_('remote bookmark %s not found!') % b)
3671 raise util.Abort(_('remote bookmark %s not found!') % b)
3676 revs.append(rb[b])
3672 revs.append(rb[b])
3677
3673
3678 if revs:
3674 if revs:
3679 try:
3675 try:
3680 revs = [other.lookup(rev) for rev in revs]
3676 revs = [other.lookup(rev) for rev in revs]
3681 except error.CapabilityError:
3677 except error.CapabilityError:
3682 err = _("other repository doesn't support revision lookup, "
3678 err = _("other repository doesn't support revision lookup, "
3683 "so a rev cannot be specified.")
3679 "so a rev cannot be specified.")
3684 raise util.Abort(err)
3680 raise util.Abort(err)
3685
3681
3686 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
3682 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
3687 bookmarks.updatefromremote(ui, repo, other)
3683 bookmarks.updatefromremote(ui, repo, other)
3688 if checkout:
3684 if checkout:
3689 checkout = str(repo.changelog.rev(other.lookup(checkout)))
3685 checkout = str(repo.changelog.rev(other.lookup(checkout)))
3690 repo._subtoppath = source
3686 repo._subtoppath = source
3691 try:
3687 try:
3692 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
3688 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
3693
3689
3694 finally:
3690 finally:
3695 del repo._subtoppath
3691 del repo._subtoppath
3696
3692
3697 # update specified bookmarks
3693 # update specified bookmarks
3698 if opts.get('bookmark'):
3694 if opts.get('bookmark'):
3699 for b in opts['bookmark']:
3695 for b in opts['bookmark']:
3700 # explicit pull overrides local bookmark if any
3696 # explicit pull overrides local bookmark if any
3701 ui.status(_("importing bookmark %s\n") % b)
3697 ui.status(_("importing bookmark %s\n") % b)
3702 repo._bookmarks[b] = repo[rb[b]].node()
3698 repo._bookmarks[b] = repo[rb[b]].node()
3703 bookmarks.write(repo)
3699 bookmarks.write(repo)
3704
3700
3705 return ret
3701 return ret
3706
3702
3707 @command('^push',
3703 @command('^push',
3708 [('f', 'force', None, _('force push')),
3704 [('f', 'force', None, _('force push')),
3709 ('r', 'rev', [],
3705 ('r', 'rev', [],
3710 _('a changeset intended to be included in the destination'),
3706 _('a changeset intended to be included in the destination'),
3711 _('REV')),
3707 _('REV')),
3712 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
3708 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
3713 ('b', 'branch', [],
3709 ('b', 'branch', [],
3714 _('a specific branch you would like to push'), _('BRANCH')),
3710 _('a specific branch you would like to push'), _('BRANCH')),
3715 ('', 'new-branch', False, _('allow pushing a new branch')),
3711 ('', 'new-branch', False, _('allow pushing a new branch')),
3716 ] + remoteopts,
3712 ] + remoteopts,
3717 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
3713 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
3718 def push(ui, repo, dest=None, **opts):
3714 def push(ui, repo, dest=None, **opts):
3719 """push changes to the specified destination
3715 """push changes to the specified destination
3720
3716
3721 Push changesets from the local repository to the specified
3717 Push changesets from the local repository to the specified
3722 destination.
3718 destination.
3723
3719
3724 This operation is symmetrical to pull: it is identical to a pull
3720 This operation is symmetrical to pull: it is identical to a pull
3725 in the destination repository from the current one.
3721 in the destination repository from the current one.
3726
3722
3727 By default, push will not allow creation of new heads at the
3723 By default, push will not allow creation of new heads at the
3728 destination, since multiple heads would make it unclear which head
3724 destination, since multiple heads would make it unclear which head
3729 to use. In this situation, it is recommended to pull and merge
3725 to use. In this situation, it is recommended to pull and merge
3730 before pushing.
3726 before pushing.
3731
3727
3732 Use --new-branch if you want to allow push to create a new named
3728 Use --new-branch if you want to allow push to create a new named
3733 branch that is not present at the destination. This allows you to
3729 branch that is not present at the destination. This allows you to
3734 only create a new branch without forcing other changes.
3730 only create a new branch without forcing other changes.
3735
3731
3736 Use -f/--force to override the default behavior and push all
3732 Use -f/--force to override the default behavior and push all
3737 changesets on all branches.
3733 changesets on all branches.
3738
3734
3739 If -r/--rev is used, the specified revision and all its ancestors
3735 If -r/--rev is used, the specified revision and all its ancestors
3740 will be pushed to the remote repository.
3736 will be pushed to the remote repository.
3741
3737
3742 Please see :hg:`help urls` for important details about ``ssh://``
3738 Please see :hg:`help urls` for important details about ``ssh://``
3743 URLs. If DESTINATION is omitted, a default path will be used.
3739 URLs. If DESTINATION is omitted, a default path will be used.
3744
3740
3745 Returns 0 if push was successful, 1 if nothing to push.
3741 Returns 0 if push was successful, 1 if nothing to push.
3746 """
3742 """
3747
3743
3748 if opts.get('bookmark'):
3744 if opts.get('bookmark'):
3749 for b in opts['bookmark']:
3745 for b in opts['bookmark']:
3750 # translate -B options to -r so changesets get pushed
3746 # translate -B options to -r so changesets get pushed
3751 if b in repo._bookmarks:
3747 if b in repo._bookmarks:
3752 opts.setdefault('rev', []).append(b)
3748 opts.setdefault('rev', []).append(b)
3753 else:
3749 else:
3754 # if we try to push a deleted bookmark, translate it to null
3750 # if we try to push a deleted bookmark, translate it to null
3755 # this lets simultaneous -r, -b options continue working
3751 # this lets simultaneous -r, -b options continue working
3756 opts.setdefault('rev', []).append("null")
3752 opts.setdefault('rev', []).append("null")
3757
3753
3758 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3754 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3759 dest, branches = hg.parseurl(dest, opts.get('branch'))
3755 dest, branches = hg.parseurl(dest, opts.get('branch'))
3760 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
3756 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
3761 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
3757 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
3762 other = hg.repository(hg.remoteui(repo, opts), dest)
3758 other = hg.repository(hg.remoteui(repo, opts), dest)
3763 if revs:
3759 if revs:
3764 revs = [repo.lookup(rev) for rev in revs]
3760 revs = [repo.lookup(rev) for rev in revs]
3765
3761
3766 repo._subtoppath = dest
3762 repo._subtoppath = dest
3767 try:
3763 try:
3768 # push subrepos depth-first for coherent ordering
3764 # push subrepos depth-first for coherent ordering
3769 c = repo['']
3765 c = repo['']
3770 subs = c.substate # only repos that are committed
3766 subs = c.substate # only repos that are committed
3771 for s in sorted(subs):
3767 for s in sorted(subs):
3772 if not c.sub(s).push(opts.get('force')):
3768 if not c.sub(s).push(opts.get('force')):
3773 return False
3769 return False
3774 finally:
3770 finally:
3775 del repo._subtoppath
3771 del repo._subtoppath
3776 result = repo.push(other, opts.get('force'), revs=revs,
3772 result = repo.push(other, opts.get('force'), revs=revs,
3777 newbranch=opts.get('new_branch'))
3773 newbranch=opts.get('new_branch'))
3778
3774
3779 result = (result == 0)
3775 result = (result == 0)
3780
3776
3781 if opts.get('bookmark'):
3777 if opts.get('bookmark'):
3782 rb = other.listkeys('bookmarks')
3778 rb = other.listkeys('bookmarks')
3783 for b in opts['bookmark']:
3779 for b in opts['bookmark']:
3784 # explicit push overrides remote bookmark if any
3780 # explicit push overrides remote bookmark if any
3785 if b in repo._bookmarks:
3781 if b in repo._bookmarks:
3786 ui.status(_("exporting bookmark %s\n") % b)
3782 ui.status(_("exporting bookmark %s\n") % b)
3787 new = repo[b].hex()
3783 new = repo[b].hex()
3788 elif b in rb:
3784 elif b in rb:
3789 ui.status(_("deleting remote bookmark %s\n") % b)
3785 ui.status(_("deleting remote bookmark %s\n") % b)
3790 new = '' # delete
3786 new = '' # delete
3791 else:
3787 else:
3792 ui.warn(_('bookmark %s does not exist on the local '
3788 ui.warn(_('bookmark %s does not exist on the local '
3793 'or remote repository!\n') % b)
3789 'or remote repository!\n') % b)
3794 return 2
3790 return 2
3795 old = rb.get(b, '')
3791 old = rb.get(b, '')
3796 r = other.pushkey('bookmarks', b, old, new)
3792 r = other.pushkey('bookmarks', b, old, new)
3797 if not r:
3793 if not r:
3798 ui.warn(_('updating bookmark %s failed!\n') % b)
3794 ui.warn(_('updating bookmark %s failed!\n') % b)
3799 if not result:
3795 if not result:
3800 result = 2
3796 result = 2
3801
3797
3802 return result
3798 return result
3803
3799
3804 @command('recover', [])
3800 @command('recover', [])
3805 def recover(ui, repo):
3801 def recover(ui, repo):
3806 """roll back an interrupted transaction
3802 """roll back an interrupted transaction
3807
3803
3808 Recover from an interrupted commit or pull.
3804 Recover from an interrupted commit or pull.
3809
3805
3810 This command tries to fix the repository status after an
3806 This command tries to fix the repository status after an
3811 interrupted operation. It should only be necessary when Mercurial
3807 interrupted operation. It should only be necessary when Mercurial
3812 suggests it.
3808 suggests it.
3813
3809
3814 Returns 0 if successful, 1 if nothing to recover or verify fails.
3810 Returns 0 if successful, 1 if nothing to recover or verify fails.
3815 """
3811 """
3816 if repo.recover():
3812 if repo.recover():
3817 return hg.verify(repo)
3813 return hg.verify(repo)
3818 return 1
3814 return 1
3819
3815
3820 @command('^remove|rm',
3816 @command('^remove|rm',
3821 [('A', 'after', None, _('record delete for missing files')),
3817 [('A', 'after', None, _('record delete for missing files')),
3822 ('f', 'force', None,
3818 ('f', 'force', None,
3823 _('remove (and delete) file even if added or modified')),
3819 _('remove (and delete) file even if added or modified')),
3824 ] + walkopts,
3820 ] + walkopts,
3825 _('[OPTION]... FILE...'))
3821 _('[OPTION]... FILE...'))
3826 def remove(ui, repo, *pats, **opts):
3822 def remove(ui, repo, *pats, **opts):
3827 """remove the specified files on the next commit
3823 """remove the specified files on the next commit
3828
3824
3829 Schedule the indicated files for removal from the repository.
3825 Schedule the indicated files for removal from the repository.
3830
3826
3831 This only removes files from the current branch, not from the
3827 This only removes files from the current branch, not from the
3832 entire project history. -A/--after can be used to remove only
3828 entire project history. -A/--after can be used to remove only
3833 files that have already been deleted, -f/--force can be used to
3829 files that have already been deleted, -f/--force can be used to
3834 force deletion, and -Af can be used to remove files from the next
3830 force deletion, and -Af can be used to remove files from the next
3835 revision without deleting them from the working directory.
3831 revision without deleting them from the working directory.
3836
3832
3837 The following table details the behavior of remove for different
3833 The following table details the behavior of remove for different
3838 file states (columns) and option combinations (rows). The file
3834 file states (columns) and option combinations (rows). The file
3839 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
3835 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
3840 reported by :hg:`status`). The actions are Warn, Remove (from
3836 reported by :hg:`status`). The actions are Warn, Remove (from
3841 branch) and Delete (from disk)::
3837 branch) and Delete (from disk)::
3842
3838
3843 A C M !
3839 A C M !
3844 none W RD W R
3840 none W RD W R
3845 -f R RD RD R
3841 -f R RD RD R
3846 -A W W W R
3842 -A W W W R
3847 -Af R R R R
3843 -Af R R R R
3848
3844
3849 This command schedules the files to be removed at the next commit.
3845 This command schedules the files to be removed at the next commit.
3850 To undo a remove before that, see :hg:`revert`.
3846 To undo a remove before that, see :hg:`revert`.
3851
3847
3852 Returns 0 on success, 1 if any warnings encountered.
3848 Returns 0 on success, 1 if any warnings encountered.
3853 """
3849 """
3854
3850
3855 ret = 0
3851 ret = 0
3856 after, force = opts.get('after'), opts.get('force')
3852 after, force = opts.get('after'), opts.get('force')
3857 if not pats and not after:
3853 if not pats and not after:
3858 raise util.Abort(_('no files specified'))
3854 raise util.Abort(_('no files specified'))
3859
3855
3860 m = cmdutil.match(repo, pats, opts)
3856 m = cmdutil.match(repo, pats, opts)
3861 s = repo.status(match=m, clean=True)
3857 s = repo.status(match=m, clean=True)
3862 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
3858 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
3863
3859
3864 for f in m.files():
3860 for f in m.files():
3865 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
3861 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
3866 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
3862 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
3867 ret = 1
3863 ret = 1
3868
3864
3869 if force:
3865 if force:
3870 remove, forget = modified + deleted + clean, added
3866 remove, forget = modified + deleted + clean, added
3871 elif after:
3867 elif after:
3872 remove, forget = deleted, []
3868 remove, forget = deleted, []
3873 for f in modified + added + clean:
3869 for f in modified + added + clean:
3874 ui.warn(_('not removing %s: file still exists (use -f'
3870 ui.warn(_('not removing %s: file still exists (use -f'
3875 ' to force removal)\n') % m.rel(f))
3871 ' to force removal)\n') % m.rel(f))
3876 ret = 1
3872 ret = 1
3877 else:
3873 else:
3878 remove, forget = deleted + clean, []
3874 remove, forget = deleted + clean, []
3879 for f in modified:
3875 for f in modified:
3880 ui.warn(_('not removing %s: file is modified (use -f'
3876 ui.warn(_('not removing %s: file is modified (use -f'
3881 ' to force removal)\n') % m.rel(f))
3877 ' to force removal)\n') % m.rel(f))
3882 ret = 1
3878 ret = 1
3883 for f in added:
3879 for f in added:
3884 ui.warn(_('not removing %s: file has been marked for add (use -f'
3880 ui.warn(_('not removing %s: file has been marked for add (use -f'
3885 ' to force removal)\n') % m.rel(f))
3881 ' to force removal)\n') % m.rel(f))
3886 ret = 1
3882 ret = 1
3887
3883
3888 for f in sorted(remove + forget):
3884 for f in sorted(remove + forget):
3889 if ui.verbose or not m.exact(f):
3885 if ui.verbose or not m.exact(f):
3890 ui.status(_('removing %s\n') % m.rel(f))
3886 ui.status(_('removing %s\n') % m.rel(f))
3891
3887
3892 repo[None].forget(forget)
3888 repo[None].forget(forget)
3893 repo[None].remove(remove, unlink=not after)
3889 repo[None].remove(remove, unlink=not after)
3894 return ret
3890 return ret
3895
3891
3896 @command('rename|move|mv',
3892 @command('rename|move|mv',
3897 [('A', 'after', None, _('record a rename that has already occurred')),
3893 [('A', 'after', None, _('record a rename that has already occurred')),
3898 ('f', 'force', None, _('forcibly copy over an existing managed file')),
3894 ('f', 'force', None, _('forcibly copy over an existing managed file')),
3899 ] + walkopts + dryrunopts,
3895 ] + walkopts + dryrunopts,
3900 _('[OPTION]... SOURCE... DEST'))
3896 _('[OPTION]... SOURCE... DEST'))
3901 def rename(ui, repo, *pats, **opts):
3897 def rename(ui, repo, *pats, **opts):
3902 """rename files; equivalent of copy + remove
3898 """rename files; equivalent of copy + remove
3903
3899
3904 Mark dest as copies of sources; mark sources for deletion. If dest
3900 Mark dest as copies of sources; mark sources for deletion. If dest
3905 is a directory, copies are put in that directory. If dest is a
3901 is a directory, copies are put in that directory. If dest is a
3906 file, there can only be one source.
3902 file, there can only be one source.
3907
3903
3908 By default, this command copies the contents of files as they
3904 By default, this command copies the contents of files as they
3909 exist in the working directory. If invoked with -A/--after, the
3905 exist in the working directory. If invoked with -A/--after, the
3910 operation is recorded, but no copying is performed.
3906 operation is recorded, but no copying is performed.
3911
3907
3912 This command takes effect at the next commit. To undo a rename
3908 This command takes effect at the next commit. To undo a rename
3913 before that, see :hg:`revert`.
3909 before that, see :hg:`revert`.
3914
3910
3915 Returns 0 on success, 1 if errors are encountered.
3911 Returns 0 on success, 1 if errors are encountered.
3916 """
3912 """
3917 wlock = repo.wlock(False)
3913 wlock = repo.wlock(False)
3918 try:
3914 try:
3919 return cmdutil.copy(ui, repo, pats, opts, rename=True)
3915 return cmdutil.copy(ui, repo, pats, opts, rename=True)
3920 finally:
3916 finally:
3921 wlock.release()
3917 wlock.release()
3922
3918
3923 @command('resolve',
3919 @command('resolve',
3924 [('a', 'all', None, _('select all unresolved files')),
3920 [('a', 'all', None, _('select all unresolved files')),
3925 ('l', 'list', None, _('list state of files needing merge')),
3921 ('l', 'list', None, _('list state of files needing merge')),
3926 ('m', 'mark', None, _('mark files as resolved')),
3922 ('m', 'mark', None, _('mark files as resolved')),
3927 ('u', 'unmark', None, _('mark files as unresolved')),
3923 ('u', 'unmark', None, _('mark files as unresolved')),
3928 ('t', 'tool', '', _('specify merge tool')),
3924 ('t', 'tool', '', _('specify merge tool')),
3929 ('n', 'no-status', None, _('hide status prefix'))]
3925 ('n', 'no-status', None, _('hide status prefix'))]
3930 + walkopts,
3926 + walkopts,
3931 _('[OPTION]... [FILE]...'))
3927 _('[OPTION]... [FILE]...'))
3932 def resolve(ui, repo, *pats, **opts):
3928 def resolve(ui, repo, *pats, **opts):
3933 """redo merges or set/view the merge status of files
3929 """redo merges or set/view the merge status of files
3934
3930
3935 Merges with unresolved conflicts are often the result of
3931 Merges with unresolved conflicts are often the result of
3936 non-interactive merging using the ``internal:merge`` configuration
3932 non-interactive merging using the ``internal:merge`` configuration
3937 setting, or a command-line merge tool like ``diff3``. The resolve
3933 setting, or a command-line merge tool like ``diff3``. The resolve
3938 command is used to manage the files involved in a merge, after
3934 command is used to manage the files involved in a merge, after
3939 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
3935 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
3940 working directory must have two parents).
3936 working directory must have two parents).
3941
3937
3942 The resolve command can be used in the following ways:
3938 The resolve command can be used in the following ways:
3943
3939
3944 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
3940 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
3945 files, discarding any previous merge attempts. Re-merging is not
3941 files, discarding any previous merge attempts. Re-merging is not
3946 performed for files already marked as resolved. Use ``--all/-a``
3942 performed for files already marked as resolved. Use ``--all/-a``
3947 to selects all unresolved files. ``--tool`` can be used to specify
3943 to selects all unresolved files. ``--tool`` can be used to specify
3948 the merge tool used for the given files. It overrides the HGMERGE
3944 the merge tool used for the given files. It overrides the HGMERGE
3949 environment variable and your configuration files.
3945 environment variable and your configuration files.
3950
3946
3951 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
3947 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
3952 (e.g. after having manually fixed-up the files). The default is
3948 (e.g. after having manually fixed-up the files). The default is
3953 to mark all unresolved files.
3949 to mark all unresolved files.
3954
3950
3955 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
3951 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
3956 default is to mark all resolved files.
3952 default is to mark all resolved files.
3957
3953
3958 - :hg:`resolve -l`: list files which had or still have conflicts.
3954 - :hg:`resolve -l`: list files which had or still have conflicts.
3959 In the printed list, ``U`` = unresolved and ``R`` = resolved.
3955 In the printed list, ``U`` = unresolved and ``R`` = resolved.
3960
3956
3961 Note that Mercurial will not let you commit files with unresolved
3957 Note that Mercurial will not let you commit files with unresolved
3962 merge conflicts. You must use :hg:`resolve -m ...` before you can
3958 merge conflicts. You must use :hg:`resolve -m ...` before you can
3963 commit after a conflicting merge.
3959 commit after a conflicting merge.
3964
3960
3965 Returns 0 on success, 1 if any files fail a resolve attempt.
3961 Returns 0 on success, 1 if any files fail a resolve attempt.
3966 """
3962 """
3967
3963
3968 all, mark, unmark, show, nostatus = \
3964 all, mark, unmark, show, nostatus = \
3969 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
3965 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
3970
3966
3971 if (show and (mark or unmark)) or (mark and unmark):
3967 if (show and (mark or unmark)) or (mark and unmark):
3972 raise util.Abort(_("too many options specified"))
3968 raise util.Abort(_("too many options specified"))
3973 if pats and all:
3969 if pats and all:
3974 raise util.Abort(_("can't specify --all and patterns"))
3970 raise util.Abort(_("can't specify --all and patterns"))
3975 if not (all or pats or show or mark or unmark):
3971 if not (all or pats or show or mark or unmark):
3976 raise util.Abort(_('no files or directories specified; '
3972 raise util.Abort(_('no files or directories specified; '
3977 'use --all to remerge all files'))
3973 'use --all to remerge all files'))
3978
3974
3979 ms = mergemod.mergestate(repo)
3975 ms = mergemod.mergestate(repo)
3980 m = cmdutil.match(repo, pats, opts)
3976 m = cmdutil.match(repo, pats, opts)
3981 ret = 0
3977 ret = 0
3982
3978
3983 for f in ms:
3979 for f in ms:
3984 if m(f):
3980 if m(f):
3985 if show:
3981 if show:
3986 if nostatus:
3982 if nostatus:
3987 ui.write("%s\n" % f)
3983 ui.write("%s\n" % f)
3988 else:
3984 else:
3989 ui.write("%s %s\n" % (ms[f].upper(), f),
3985 ui.write("%s %s\n" % (ms[f].upper(), f),
3990 label='resolve.' +
3986 label='resolve.' +
3991 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3987 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
3992 elif mark:
3988 elif mark:
3993 ms.mark(f, "r")
3989 ms.mark(f, "r")
3994 elif unmark:
3990 elif unmark:
3995 ms.mark(f, "u")
3991 ms.mark(f, "u")
3996 else:
3992 else:
3997 wctx = repo[None]
3993 wctx = repo[None]
3998 mctx = wctx.parents()[-1]
3994 mctx = wctx.parents()[-1]
3999
3995
4000 # backup pre-resolve (merge uses .orig for its own purposes)
3996 # backup pre-resolve (merge uses .orig for its own purposes)
4001 a = repo.wjoin(f)
3997 a = repo.wjoin(f)
4002 util.copyfile(a, a + ".resolve")
3998 util.copyfile(a, a + ".resolve")
4003
3999
4004 try:
4000 try:
4005 # resolve file
4001 # resolve file
4006 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4002 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4007 if ms.resolve(f, wctx, mctx):
4003 if ms.resolve(f, wctx, mctx):
4008 ret = 1
4004 ret = 1
4009 finally:
4005 finally:
4010 ui.setconfig('ui', 'forcemerge', '')
4006 ui.setconfig('ui', 'forcemerge', '')
4011
4007
4012 # replace filemerge's .orig file with our resolve file
4008 # replace filemerge's .orig file with our resolve file
4013 util.rename(a + ".resolve", a + ".orig")
4009 util.rename(a + ".resolve", a + ".orig")
4014
4010
4015 ms.commit()
4011 ms.commit()
4016 return ret
4012 return ret
4017
4013
4018 @command('revert',
4014 @command('revert',
4019 [('a', 'all', None, _('revert all changes when no arguments given')),
4015 [('a', 'all', None, _('revert all changes when no arguments given')),
4020 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4016 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4021 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4017 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4022 ('', 'no-backup', None, _('do not save backup copies of files')),
4018 ('', 'no-backup', None, _('do not save backup copies of files')),
4023 ] + walkopts + dryrunopts,
4019 ] + walkopts + dryrunopts,
4024 _('[OPTION]... [-r REV] [NAME]...'))
4020 _('[OPTION]... [-r REV] [NAME]...'))
4025 def revert(ui, repo, *pats, **opts):
4021 def revert(ui, repo, *pats, **opts):
4026 """restore individual files or directories to an earlier state
4022 """restore individual files or directories to an earlier state
4027
4023
4028 .. note::
4024 .. note::
4029 This command is most likely not what you are looking for.
4025 This command is most likely not what you are looking for.
4030 Revert will partially overwrite content in the working
4026 Revert will partially overwrite content in the working
4031 directory without changing the working directory parents. Use
4027 directory without changing the working directory parents. Use
4032 :hg:`update -r rev` to check out earlier revisions, or
4028 :hg:`update -r rev` to check out earlier revisions, or
4033 :hg:`update --clean .` to undo a merge which has added another
4029 :hg:`update --clean .` to undo a merge which has added another
4034 parent.
4030 parent.
4035
4031
4036 With no revision specified, revert the named files or directories
4032 With no revision specified, revert the named files or directories
4037 to the contents they had in the parent of the working directory.
4033 to the contents they had in the parent of the working directory.
4038 This restores the contents of the affected files to an unmodified
4034 This restores the contents of the affected files to an unmodified
4039 state and unschedules adds, removes, copies, and renames. If the
4035 state and unschedules adds, removes, copies, and renames. If the
4040 working directory has two parents, you must explicitly specify a
4036 working directory has two parents, you must explicitly specify a
4041 revision.
4037 revision.
4042
4038
4043 Using the -r/--rev option, revert the given files or directories
4039 Using the -r/--rev option, revert the given files or directories
4044 to their contents as of a specific revision. This can be helpful
4040 to their contents as of a specific revision. This can be helpful
4045 to "roll back" some or all of an earlier change. See :hg:`help
4041 to "roll back" some or all of an earlier change. See :hg:`help
4046 dates` for a list of formats valid for -d/--date.
4042 dates` for a list of formats valid for -d/--date.
4047
4043
4048 Revert modifies the working directory. It does not commit any
4044 Revert modifies the working directory. It does not commit any
4049 changes, or change the parent of the working directory. If you
4045 changes, or change the parent of the working directory. If you
4050 revert to a revision other than the parent of the working
4046 revert to a revision other than the parent of the working
4051 directory, the reverted files will thus appear modified
4047 directory, the reverted files will thus appear modified
4052 afterwards.
4048 afterwards.
4053
4049
4054 If a file has been deleted, it is restored. Files scheduled for
4050 If a file has been deleted, it is restored. Files scheduled for
4055 addition are just unscheduled and left as they are. If the
4051 addition are just unscheduled and left as they are. If the
4056 executable mode of a file was changed, it is reset.
4052 executable mode of a file was changed, it is reset.
4057
4053
4058 If names are given, all files matching the names are reverted.
4054 If names are given, all files matching the names are reverted.
4059 If no arguments are given, no files are reverted.
4055 If no arguments are given, no files are reverted.
4060
4056
4061 Modified files are saved with a .orig suffix before reverting.
4057 Modified files are saved with a .orig suffix before reverting.
4062 To disable these backups, use --no-backup.
4058 To disable these backups, use --no-backup.
4063
4059
4064 Returns 0 on success.
4060 Returns 0 on success.
4065 """
4061 """
4066
4062
4067 if opts.get("date"):
4063 if opts.get("date"):
4068 if opts.get("rev"):
4064 if opts.get("rev"):
4069 raise util.Abort(_("you can't specify a revision and a date"))
4065 raise util.Abort(_("you can't specify a revision and a date"))
4070 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4066 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4071
4067
4072 parent, p2 = repo.dirstate.parents()
4068 parent, p2 = repo.dirstate.parents()
4073 if not opts.get('rev') and p2 != nullid:
4069 if not opts.get('rev') and p2 != nullid:
4074 raise util.Abort(_('uncommitted merge - '
4070 raise util.Abort(_('uncommitted merge - '
4075 'use "hg update", see "hg help revert"'))
4071 'use "hg update", see "hg help revert"'))
4076
4072
4077 if not pats and not opts.get('all'):
4073 if not pats and not opts.get('all'):
4078 raise util.Abort(_('no files or directories specified; '
4074 raise util.Abort(_('no files or directories specified; '
4079 'use --all to revert the whole repo'))
4075 'use --all to revert the whole repo'))
4080
4076
4081 ctx = cmdutil.revsingle(repo, opts.get('rev'))
4077 ctx = cmdutil.revsingle(repo, opts.get('rev'))
4082 node = ctx.node()
4078 node = ctx.node()
4083 mf = ctx.manifest()
4079 mf = ctx.manifest()
4084 if node == parent:
4080 if node == parent:
4085 pmf = mf
4081 pmf = mf
4086 else:
4082 else:
4087 pmf = None
4083 pmf = None
4088
4084
4089 # need all matching names in dirstate and manifest of target rev,
4085 # need all matching names in dirstate and manifest of target rev,
4090 # so have to walk both. do not print errors if files exist in one
4086 # so have to walk both. do not print errors if files exist in one
4091 # but not other.
4087 # but not other.
4092
4088
4093 names = {}
4089 names = {}
4094
4090
4095 wlock = repo.wlock()
4091 wlock = repo.wlock()
4096 try:
4092 try:
4097 # walk dirstate.
4093 # walk dirstate.
4098
4094
4099 m = cmdutil.match(repo, pats, opts)
4095 m = cmdutil.match(repo, pats, opts)
4100 m.bad = lambda x, y: False
4096 m.bad = lambda x, y: False
4101 for abs in repo.walk(m):
4097 for abs in repo.walk(m):
4102 names[abs] = m.rel(abs), m.exact(abs)
4098 names[abs] = m.rel(abs), m.exact(abs)
4103
4099
4104 # walk target manifest.
4100 # walk target manifest.
4105
4101
4106 def badfn(path, msg):
4102 def badfn(path, msg):
4107 if path in names:
4103 if path in names:
4108 return
4104 return
4109 path_ = path + '/'
4105 path_ = path + '/'
4110 for f in names:
4106 for f in names:
4111 if f.startswith(path_):
4107 if f.startswith(path_):
4112 return
4108 return
4113 ui.warn("%s: %s\n" % (m.rel(path), msg))
4109 ui.warn("%s: %s\n" % (m.rel(path), msg))
4114
4110
4115 m = cmdutil.match(repo, pats, opts)
4111 m = cmdutil.match(repo, pats, opts)
4116 m.bad = badfn
4112 m.bad = badfn
4117 for abs in repo[node].walk(m):
4113 for abs in repo[node].walk(m):
4118 if abs not in names:
4114 if abs not in names:
4119 names[abs] = m.rel(abs), m.exact(abs)
4115 names[abs] = m.rel(abs), m.exact(abs)
4120
4116
4121 m = cmdutil.matchfiles(repo, names)
4117 m = cmdutil.matchfiles(repo, names)
4122 changes = repo.status(match=m)[:4]
4118 changes = repo.status(match=m)[:4]
4123 modified, added, removed, deleted = map(set, changes)
4119 modified, added, removed, deleted = map(set, changes)
4124
4120
4125 # if f is a rename, also revert the source
4121 # if f is a rename, also revert the source
4126 cwd = repo.getcwd()
4122 cwd = repo.getcwd()
4127 for f in added:
4123 for f in added:
4128 src = repo.dirstate.copied(f)
4124 src = repo.dirstate.copied(f)
4129 if src and src not in names and repo.dirstate[src] == 'r':
4125 if src and src not in names and repo.dirstate[src] == 'r':
4130 removed.add(src)
4126 removed.add(src)
4131 names[src] = (repo.pathto(src, cwd), True)
4127 names[src] = (repo.pathto(src, cwd), True)
4132
4128
4133 def removeforget(abs):
4129 def removeforget(abs):
4134 if repo.dirstate[abs] == 'a':
4130 if repo.dirstate[abs] == 'a':
4135 return _('forgetting %s\n')
4131 return _('forgetting %s\n')
4136 return _('removing %s\n')
4132 return _('removing %s\n')
4137
4133
4138 revert = ([], _('reverting %s\n'))
4134 revert = ([], _('reverting %s\n'))
4139 add = ([], _('adding %s\n'))
4135 add = ([], _('adding %s\n'))
4140 remove = ([], removeforget)
4136 remove = ([], removeforget)
4141 undelete = ([], _('undeleting %s\n'))
4137 undelete = ([], _('undeleting %s\n'))
4142
4138
4143 disptable = (
4139 disptable = (
4144 # dispatch table:
4140 # dispatch table:
4145 # file state
4141 # file state
4146 # action if in target manifest
4142 # action if in target manifest
4147 # action if not in target manifest
4143 # action if not in target manifest
4148 # make backup if in target manifest
4144 # make backup if in target manifest
4149 # make backup if not in target manifest
4145 # make backup if not in target manifest
4150 (modified, revert, remove, True, True),
4146 (modified, revert, remove, True, True),
4151 (added, revert, remove, True, False),
4147 (added, revert, remove, True, False),
4152 (removed, undelete, None, False, False),
4148 (removed, undelete, None, False, False),
4153 (deleted, revert, remove, False, False),
4149 (deleted, revert, remove, False, False),
4154 )
4150 )
4155
4151
4156 for abs, (rel, exact) in sorted(names.items()):
4152 for abs, (rel, exact) in sorted(names.items()):
4157 mfentry = mf.get(abs)
4153 mfentry = mf.get(abs)
4158 target = repo.wjoin(abs)
4154 target = repo.wjoin(abs)
4159 def handle(xlist, dobackup):
4155 def handle(xlist, dobackup):
4160 xlist[0].append(abs)
4156 xlist[0].append(abs)
4161 if (dobackup and not opts.get('no_backup') and
4157 if (dobackup and not opts.get('no_backup') and
4162 os.path.lexists(target)):
4158 os.path.lexists(target)):
4163 bakname = "%s.orig" % rel
4159 bakname = "%s.orig" % rel
4164 ui.note(_('saving current version of %s as %s\n') %
4160 ui.note(_('saving current version of %s as %s\n') %
4165 (rel, bakname))
4161 (rel, bakname))
4166 if not opts.get('dry_run'):
4162 if not opts.get('dry_run'):
4167 util.rename(target, bakname)
4163 util.rename(target, bakname)
4168 if ui.verbose or not exact:
4164 if ui.verbose or not exact:
4169 msg = xlist[1]
4165 msg = xlist[1]
4170 if not isinstance(msg, basestring):
4166 if not isinstance(msg, basestring):
4171 msg = msg(abs)
4167 msg = msg(abs)
4172 ui.status(msg % rel)
4168 ui.status(msg % rel)
4173 for table, hitlist, misslist, backuphit, backupmiss in disptable:
4169 for table, hitlist, misslist, backuphit, backupmiss in disptable:
4174 if abs not in table:
4170 if abs not in table:
4175 continue
4171 continue
4176 # file has changed in dirstate
4172 # file has changed in dirstate
4177 if mfentry:
4173 if mfentry:
4178 handle(hitlist, backuphit)
4174 handle(hitlist, backuphit)
4179 elif misslist is not None:
4175 elif misslist is not None:
4180 handle(misslist, backupmiss)
4176 handle(misslist, backupmiss)
4181 break
4177 break
4182 else:
4178 else:
4183 if abs not in repo.dirstate:
4179 if abs not in repo.dirstate:
4184 if mfentry:
4180 if mfentry:
4185 handle(add, True)
4181 handle(add, True)
4186 elif exact:
4182 elif exact:
4187 ui.warn(_('file not managed: %s\n') % rel)
4183 ui.warn(_('file not managed: %s\n') % rel)
4188 continue
4184 continue
4189 # file has not changed in dirstate
4185 # file has not changed in dirstate
4190 if node == parent:
4186 if node == parent:
4191 if exact:
4187 if exact:
4192 ui.warn(_('no changes needed to %s\n') % rel)
4188 ui.warn(_('no changes needed to %s\n') % rel)
4193 continue
4189 continue
4194 if pmf is None:
4190 if pmf is None:
4195 # only need parent manifest in this unlikely case,
4191 # only need parent manifest in this unlikely case,
4196 # so do not read by default
4192 # so do not read by default
4197 pmf = repo[parent].manifest()
4193 pmf = repo[parent].manifest()
4198 if abs in pmf:
4194 if abs in pmf:
4199 if mfentry:
4195 if mfentry:
4200 # if version of file is same in parent and target
4196 # if version of file is same in parent and target
4201 # manifests, do nothing
4197 # manifests, do nothing
4202 if (pmf[abs] != mfentry or
4198 if (pmf[abs] != mfentry or
4203 pmf.flags(abs) != mf.flags(abs)):
4199 pmf.flags(abs) != mf.flags(abs)):
4204 handle(revert, False)
4200 handle(revert, False)
4205 else:
4201 else:
4206 handle(remove, False)
4202 handle(remove, False)
4207
4203
4208 if not opts.get('dry_run'):
4204 if not opts.get('dry_run'):
4209 def checkout(f):
4205 def checkout(f):
4210 fc = ctx[f]
4206 fc = ctx[f]
4211 repo.wwrite(f, fc.data(), fc.flags())
4207 repo.wwrite(f, fc.data(), fc.flags())
4212
4208
4213 audit_path = scmutil.pathauditor(repo.root)
4209 audit_path = scmutil.pathauditor(repo.root)
4214 for f in remove[0]:
4210 for f in remove[0]:
4215 if repo.dirstate[f] == 'a':
4211 if repo.dirstate[f] == 'a':
4216 repo.dirstate.forget(f)
4212 repo.dirstate.forget(f)
4217 continue
4213 continue
4218 audit_path(f)
4214 audit_path(f)
4219 try:
4215 try:
4220 util.unlinkpath(repo.wjoin(f))
4216 util.unlinkpath(repo.wjoin(f))
4221 except OSError:
4217 except OSError:
4222 pass
4218 pass
4223 repo.dirstate.remove(f)
4219 repo.dirstate.remove(f)
4224
4220
4225 normal = None
4221 normal = None
4226 if node == parent:
4222 if node == parent:
4227 # We're reverting to our parent. If possible, we'd like status
4223 # We're reverting to our parent. If possible, we'd like status
4228 # to report the file as clean. We have to use normallookup for
4224 # to report the file as clean. We have to use normallookup for
4229 # merges to avoid losing information about merged/dirty files.
4225 # merges to avoid losing information about merged/dirty files.
4230 if p2 != nullid:
4226 if p2 != nullid:
4231 normal = repo.dirstate.normallookup
4227 normal = repo.dirstate.normallookup
4232 else:
4228 else:
4233 normal = repo.dirstate.normal
4229 normal = repo.dirstate.normal
4234 for f in revert[0]:
4230 for f in revert[0]:
4235 checkout(f)
4231 checkout(f)
4236 if normal:
4232 if normal:
4237 normal(f)
4233 normal(f)
4238
4234
4239 for f in add[0]:
4235 for f in add[0]:
4240 checkout(f)
4236 checkout(f)
4241 repo.dirstate.add(f)
4237 repo.dirstate.add(f)
4242
4238
4243 normal = repo.dirstate.normallookup
4239 normal = repo.dirstate.normallookup
4244 if node == parent and p2 == nullid:
4240 if node == parent and p2 == nullid:
4245 normal = repo.dirstate.normal
4241 normal = repo.dirstate.normal
4246 for f in undelete[0]:
4242 for f in undelete[0]:
4247 checkout(f)
4243 checkout(f)
4248 normal(f)
4244 normal(f)
4249
4245
4250 finally:
4246 finally:
4251 wlock.release()
4247 wlock.release()
4252
4248
4253 @command('rollback', dryrunopts)
4249 @command('rollback', dryrunopts)
4254 def rollback(ui, repo, **opts):
4250 def rollback(ui, repo, **opts):
4255 """roll back the last transaction (dangerous)
4251 """roll back the last transaction (dangerous)
4256
4252
4257 This command should be used with care. There is only one level of
4253 This command should be used with care. There is only one level of
4258 rollback, and there is no way to undo a rollback. It will also
4254 rollback, and there is no way to undo a rollback. It will also
4259 restore the dirstate at the time of the last transaction, losing
4255 restore the dirstate at the time of the last transaction, losing
4260 any dirstate changes since that time. This command does not alter
4256 any dirstate changes since that time. This command does not alter
4261 the working directory.
4257 the working directory.
4262
4258
4263 Transactions are used to encapsulate the effects of all commands
4259 Transactions are used to encapsulate the effects of all commands
4264 that create new changesets or propagate existing changesets into a
4260 that create new changesets or propagate existing changesets into a
4265 repository. For example, the following commands are transactional,
4261 repository. For example, the following commands are transactional,
4266 and their effects can be rolled back:
4262 and their effects can be rolled back:
4267
4263
4268 - commit
4264 - commit
4269 - import
4265 - import
4270 - pull
4266 - pull
4271 - push (with this repository as the destination)
4267 - push (with this repository as the destination)
4272 - unbundle
4268 - unbundle
4273
4269
4274 This command is not intended for use on public repositories. Once
4270 This command is not intended for use on public repositories. Once
4275 changes are visible for pull by other users, rolling a transaction
4271 changes are visible for pull by other users, rolling a transaction
4276 back locally is ineffective (someone else may already have pulled
4272 back locally is ineffective (someone else may already have pulled
4277 the changes). Furthermore, a race is possible with readers of the
4273 the changes). Furthermore, a race is possible with readers of the
4278 repository; for example an in-progress pull from the repository
4274 repository; for example an in-progress pull from the repository
4279 may fail if a rollback is performed.
4275 may fail if a rollback is performed.
4280
4276
4281 Returns 0 on success, 1 if no rollback data is available.
4277 Returns 0 on success, 1 if no rollback data is available.
4282 """
4278 """
4283 return repo.rollback(opts.get('dry_run'))
4279 return repo.rollback(opts.get('dry_run'))
4284
4280
4285 @command('root', [])
4281 @command('root', [])
4286 def root(ui, repo):
4282 def root(ui, repo):
4287 """print the root (top) of the current working directory
4283 """print the root (top) of the current working directory
4288
4284
4289 Print the root directory of the current repository.
4285 Print the root directory of the current repository.
4290
4286
4291 Returns 0 on success.
4287 Returns 0 on success.
4292 """
4288 """
4293 ui.write(repo.root + "\n")
4289 ui.write(repo.root + "\n")
4294
4290
4295 @command('^serve',
4291 @command('^serve',
4296 [('A', 'accesslog', '', _('name of access log file to write to'),
4292 [('A', 'accesslog', '', _('name of access log file to write to'),
4297 _('FILE')),
4293 _('FILE')),
4298 ('d', 'daemon', None, _('run server in background')),
4294 ('d', 'daemon', None, _('run server in background')),
4299 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4295 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4300 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4296 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4301 # use string type, then we can check if something was passed
4297 # use string type, then we can check if something was passed
4302 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4298 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4303 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4299 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4304 _('ADDR')),
4300 _('ADDR')),
4305 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4301 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4306 _('PREFIX')),
4302 _('PREFIX')),
4307 ('n', 'name', '',
4303 ('n', 'name', '',
4308 _('name to show in web pages (default: working directory)'), _('NAME')),
4304 _('name to show in web pages (default: working directory)'), _('NAME')),
4309 ('', 'web-conf', '',
4305 ('', 'web-conf', '',
4310 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
4306 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
4311 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4307 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4312 _('FILE')),
4308 _('FILE')),
4313 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4309 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4314 ('', 'stdio', None, _('for remote clients')),
4310 ('', 'stdio', None, _('for remote clients')),
4315 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4311 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4316 ('', 'style', '', _('template style to use'), _('STYLE')),
4312 ('', 'style', '', _('template style to use'), _('STYLE')),
4317 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4313 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4318 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
4314 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
4319 _('[OPTION]...'))
4315 _('[OPTION]...'))
4320 def serve(ui, repo, **opts):
4316 def serve(ui, repo, **opts):
4321 """start stand-alone webserver
4317 """start stand-alone webserver
4322
4318
4323 Start a local HTTP repository browser and pull server. You can use
4319 Start a local HTTP repository browser and pull server. You can use
4324 this for ad-hoc sharing and browsing of repositories. It is
4320 this for ad-hoc sharing and browsing of repositories. It is
4325 recommended to use a real web server to serve a repository for
4321 recommended to use a real web server to serve a repository for
4326 longer periods of time.
4322 longer periods of time.
4327
4323
4328 Please note that the server does not implement access control.
4324 Please note that the server does not implement access control.
4329 This means that, by default, anybody can read from the server and
4325 This means that, by default, anybody can read from the server and
4330 nobody can write to it by default. Set the ``web.allow_push``
4326 nobody can write to it by default. Set the ``web.allow_push``
4331 option to ``*`` to allow everybody to push to the server. You
4327 option to ``*`` to allow everybody to push to the server. You
4332 should use a real web server if you need to authenticate users.
4328 should use a real web server if you need to authenticate users.
4333
4329
4334 By default, the server logs accesses to stdout and errors to
4330 By default, the server logs accesses to stdout and errors to
4335 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4331 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4336 files.
4332 files.
4337
4333
4338 To have the server choose a free port number to listen on, specify
4334 To have the server choose a free port number to listen on, specify
4339 a port number of 0; in this case, the server will print the port
4335 a port number of 0; in this case, the server will print the port
4340 number it uses.
4336 number it uses.
4341
4337
4342 Returns 0 on success.
4338 Returns 0 on success.
4343 """
4339 """
4344
4340
4345 if opts["stdio"]:
4341 if opts["stdio"]:
4346 if repo is None:
4342 if repo is None:
4347 raise error.RepoError(_("There is no Mercurial repository here"
4343 raise error.RepoError(_("There is no Mercurial repository here"
4348 " (.hg not found)"))
4344 " (.hg not found)"))
4349 s = sshserver.sshserver(ui, repo)
4345 s = sshserver.sshserver(ui, repo)
4350 s.serve_forever()
4346 s.serve_forever()
4351
4347
4352 # this way we can check if something was given in the command-line
4348 # this way we can check if something was given in the command-line
4353 if opts.get('port'):
4349 if opts.get('port'):
4354 opts['port'] = util.getport(opts.get('port'))
4350 opts['port'] = util.getport(opts.get('port'))
4355
4351
4356 baseui = repo and repo.baseui or ui
4352 baseui = repo and repo.baseui or ui
4357 optlist = ("name templates style address port prefix ipv6"
4353 optlist = ("name templates style address port prefix ipv6"
4358 " accesslog errorlog certificate encoding")
4354 " accesslog errorlog certificate encoding")
4359 for o in optlist.split():
4355 for o in optlist.split():
4360 val = opts.get(o, '')
4356 val = opts.get(o, '')
4361 if val in (None, ''): # should check against default options instead
4357 if val in (None, ''): # should check against default options instead
4362 continue
4358 continue
4363 baseui.setconfig("web", o, val)
4359 baseui.setconfig("web", o, val)
4364 if repo and repo.ui != baseui:
4360 if repo and repo.ui != baseui:
4365 repo.ui.setconfig("web", o, val)
4361 repo.ui.setconfig("web", o, val)
4366
4362
4367 o = opts.get('web_conf') or opts.get('webdir_conf')
4363 o = opts.get('web_conf') or opts.get('webdir_conf')
4368 if not o:
4364 if not o:
4369 if not repo:
4365 if not repo:
4370 raise error.RepoError(_("There is no Mercurial repository"
4366 raise error.RepoError(_("There is no Mercurial repository"
4371 " here (.hg not found)"))
4367 " here (.hg not found)"))
4372 o = repo.root
4368 o = repo.root
4373
4369
4374 app = hgweb.hgweb(o, baseui=ui)
4370 app = hgweb.hgweb(o, baseui=ui)
4375
4371
4376 class service(object):
4372 class service(object):
4377 def init(self):
4373 def init(self):
4378 util.setsignalhandler()
4374 util.setsignalhandler()
4379 self.httpd = hgweb.server.create_server(ui, app)
4375 self.httpd = hgweb.server.create_server(ui, app)
4380
4376
4381 if opts['port'] and not ui.verbose:
4377 if opts['port'] and not ui.verbose:
4382 return
4378 return
4383
4379
4384 if self.httpd.prefix:
4380 if self.httpd.prefix:
4385 prefix = self.httpd.prefix.strip('/') + '/'
4381 prefix = self.httpd.prefix.strip('/') + '/'
4386 else:
4382 else:
4387 prefix = ''
4383 prefix = ''
4388
4384
4389 port = ':%d' % self.httpd.port
4385 port = ':%d' % self.httpd.port
4390 if port == ':80':
4386 if port == ':80':
4391 port = ''
4387 port = ''
4392
4388
4393 bindaddr = self.httpd.addr
4389 bindaddr = self.httpd.addr
4394 if bindaddr == '0.0.0.0':
4390 if bindaddr == '0.0.0.0':
4395 bindaddr = '*'
4391 bindaddr = '*'
4396 elif ':' in bindaddr: # IPv6
4392 elif ':' in bindaddr: # IPv6
4397 bindaddr = '[%s]' % bindaddr
4393 bindaddr = '[%s]' % bindaddr
4398
4394
4399 fqaddr = self.httpd.fqaddr
4395 fqaddr = self.httpd.fqaddr
4400 if ':' in fqaddr:
4396 if ':' in fqaddr:
4401 fqaddr = '[%s]' % fqaddr
4397 fqaddr = '[%s]' % fqaddr
4402 if opts['port']:
4398 if opts['port']:
4403 write = ui.status
4399 write = ui.status
4404 else:
4400 else:
4405 write = ui.write
4401 write = ui.write
4406 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
4402 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
4407 (fqaddr, port, prefix, bindaddr, self.httpd.port))
4403 (fqaddr, port, prefix, bindaddr, self.httpd.port))
4408
4404
4409 def run(self):
4405 def run(self):
4410 self.httpd.serve_forever()
4406 self.httpd.serve_forever()
4411
4407
4412 service = service()
4408 service = service()
4413
4409
4414 cmdutil.service(opts, initfn=service.init, runfn=service.run)
4410 cmdutil.service(opts, initfn=service.init, runfn=service.run)
4415
4411
4416 @command('showconfig|debugconfig',
4412 @command('showconfig|debugconfig',
4417 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4413 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4418 _('[-u] [NAME]...'))
4414 _('[-u] [NAME]...'))
4419 def showconfig(ui, repo, *values, **opts):
4415 def showconfig(ui, repo, *values, **opts):
4420 """show combined config settings from all hgrc files
4416 """show combined config settings from all hgrc files
4421
4417
4422 With no arguments, print names and values of all config items.
4418 With no arguments, print names and values of all config items.
4423
4419
4424 With one argument of the form section.name, print just the value
4420 With one argument of the form section.name, print just the value
4425 of that config item.
4421 of that config item.
4426
4422
4427 With multiple arguments, print names and values of all config
4423 With multiple arguments, print names and values of all config
4428 items with matching section names.
4424 items with matching section names.
4429
4425
4430 With --debug, the source (filename and line number) is printed
4426 With --debug, the source (filename and line number) is printed
4431 for each config item.
4427 for each config item.
4432
4428
4433 Returns 0 on success.
4429 Returns 0 on success.
4434 """
4430 """
4435
4431
4436 for f in scmutil.rcpath():
4432 for f in scmutil.rcpath():
4437 ui.debug(_('read config from: %s\n') % f)
4433 ui.debug(_('read config from: %s\n') % f)
4438 untrusted = bool(opts.get('untrusted'))
4434 untrusted = bool(opts.get('untrusted'))
4439 if values:
4435 if values:
4440 sections = [v for v in values if '.' not in v]
4436 sections = [v for v in values if '.' not in v]
4441 items = [v for v in values if '.' in v]
4437 items = [v for v in values if '.' in v]
4442 if len(items) > 1 or items and sections:
4438 if len(items) > 1 or items and sections:
4443 raise util.Abort(_('only one config item permitted'))
4439 raise util.Abort(_('only one config item permitted'))
4444 for section, name, value in ui.walkconfig(untrusted=untrusted):
4440 for section, name, value in ui.walkconfig(untrusted=untrusted):
4445 value = str(value).replace('\n', '\\n')
4441 value = str(value).replace('\n', '\\n')
4446 sectname = section + '.' + name
4442 sectname = section + '.' + name
4447 if values:
4443 if values:
4448 for v in values:
4444 for v in values:
4449 if v == section:
4445 if v == section:
4450 ui.debug('%s: ' %
4446 ui.debug('%s: ' %
4451 ui.configsource(section, name, untrusted))
4447 ui.configsource(section, name, untrusted))
4452 ui.write('%s=%s\n' % (sectname, value))
4448 ui.write('%s=%s\n' % (sectname, value))
4453 elif v == sectname:
4449 elif v == sectname:
4454 ui.debug('%s: ' %
4450 ui.debug('%s: ' %
4455 ui.configsource(section, name, untrusted))
4451 ui.configsource(section, name, untrusted))
4456 ui.write(value, '\n')
4452 ui.write(value, '\n')
4457 else:
4453 else:
4458 ui.debug('%s: ' %
4454 ui.debug('%s: ' %
4459 ui.configsource(section, name, untrusted))
4455 ui.configsource(section, name, untrusted))
4460 ui.write('%s=%s\n' % (sectname, value))
4456 ui.write('%s=%s\n' % (sectname, value))
4461
4457
4462 @command('^status|st',
4458 @command('^status|st',
4463 [('A', 'all', None, _('show status of all files')),
4459 [('A', 'all', None, _('show status of all files')),
4464 ('m', 'modified', None, _('show only modified files')),
4460 ('m', 'modified', None, _('show only modified files')),
4465 ('a', 'added', None, _('show only added files')),
4461 ('a', 'added', None, _('show only added files')),
4466 ('r', 'removed', None, _('show only removed files')),
4462 ('r', 'removed', None, _('show only removed files')),
4467 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4463 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4468 ('c', 'clean', None, _('show only files without changes')),
4464 ('c', 'clean', None, _('show only files without changes')),
4469 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4465 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4470 ('i', 'ignored', None, _('show only ignored files')),
4466 ('i', 'ignored', None, _('show only ignored files')),
4471 ('n', 'no-status', None, _('hide status prefix')),
4467 ('n', 'no-status', None, _('hide status prefix')),
4472 ('C', 'copies', None, _('show source of copied files')),
4468 ('C', 'copies', None, _('show source of copied files')),
4473 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4469 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4474 ('', 'rev', [], _('show difference from revision'), _('REV')),
4470 ('', 'rev', [], _('show difference from revision'), _('REV')),
4475 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4471 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4476 ] + walkopts + subrepoopts,
4472 ] + walkopts + subrepoopts,
4477 _('[OPTION]... [FILE]...'))
4473 _('[OPTION]... [FILE]...'))
4478 def status(ui, repo, *pats, **opts):
4474 def status(ui, repo, *pats, **opts):
4479 """show changed files in the working directory
4475 """show changed files in the working directory
4480
4476
4481 Show status of files in the repository. If names are given, only
4477 Show status of files in the repository. If names are given, only
4482 files that match are shown. Files that are clean or ignored or
4478 files that match are shown. Files that are clean or ignored or
4483 the source of a copy/move operation, are not listed unless
4479 the source of a copy/move operation, are not listed unless
4484 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4480 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4485 Unless options described with "show only ..." are given, the
4481 Unless options described with "show only ..." are given, the
4486 options -mardu are used.
4482 options -mardu are used.
4487
4483
4488 Option -q/--quiet hides untracked (unknown and ignored) files
4484 Option -q/--quiet hides untracked (unknown and ignored) files
4489 unless explicitly requested with -u/--unknown or -i/--ignored.
4485 unless explicitly requested with -u/--unknown or -i/--ignored.
4490
4486
4491 .. note::
4487 .. note::
4492 status may appear to disagree with diff if permissions have
4488 status may appear to disagree with diff if permissions have
4493 changed or a merge has occurred. The standard diff format does
4489 changed or a merge has occurred. The standard diff format does
4494 not report permission changes and diff only reports changes
4490 not report permission changes and diff only reports changes
4495 relative to one merge parent.
4491 relative to one merge parent.
4496
4492
4497 If one revision is given, it is used as the base revision.
4493 If one revision is given, it is used as the base revision.
4498 If two revisions are given, the differences between them are
4494 If two revisions are given, the differences between them are
4499 shown. The --change option can also be used as a shortcut to list
4495 shown. The --change option can also be used as a shortcut to list
4500 the changed files of a revision from its first parent.
4496 the changed files of a revision from its first parent.
4501
4497
4502 The codes used to show the status of files are::
4498 The codes used to show the status of files are::
4503
4499
4504 M = modified
4500 M = modified
4505 A = added
4501 A = added
4506 R = removed
4502 R = removed
4507 C = clean
4503 C = clean
4508 ! = missing (deleted by non-hg command, but still tracked)
4504 ! = missing (deleted by non-hg command, but still tracked)
4509 ? = not tracked
4505 ? = not tracked
4510 I = ignored
4506 I = ignored
4511 = origin of the previous file listed as A (added)
4507 = origin of the previous file listed as A (added)
4512
4508
4513 Returns 0 on success.
4509 Returns 0 on success.
4514 """
4510 """
4515
4511
4516 revs = opts.get('rev')
4512 revs = opts.get('rev')
4517 change = opts.get('change')
4513 change = opts.get('change')
4518
4514
4519 if revs and change:
4515 if revs and change:
4520 msg = _('cannot specify --rev and --change at the same time')
4516 msg = _('cannot specify --rev and --change at the same time')
4521 raise util.Abort(msg)
4517 raise util.Abort(msg)
4522 elif change:
4518 elif change:
4523 node2 = repo.lookup(change)
4519 node2 = repo.lookup(change)
4524 node1 = repo[node2].p1().node()
4520 node1 = repo[node2].p1().node()
4525 else:
4521 else:
4526 node1, node2 = cmdutil.revpair(repo, revs)
4522 node1, node2 = cmdutil.revpair(repo, revs)
4527
4523
4528 cwd = (pats and repo.getcwd()) or ''
4524 cwd = (pats and repo.getcwd()) or ''
4529 end = opts.get('print0') and '\0' or '\n'
4525 end = opts.get('print0') and '\0' or '\n'
4530 copy = {}
4526 copy = {}
4531 states = 'modified added removed deleted unknown ignored clean'.split()
4527 states = 'modified added removed deleted unknown ignored clean'.split()
4532 show = [k for k in states if opts.get(k)]
4528 show = [k for k in states if opts.get(k)]
4533 if opts.get('all'):
4529 if opts.get('all'):
4534 show += ui.quiet and (states[:4] + ['clean']) or states
4530 show += ui.quiet and (states[:4] + ['clean']) or states
4535 if not show:
4531 if not show:
4536 show = ui.quiet and states[:4] or states[:5]
4532 show = ui.quiet and states[:4] or states[:5]
4537
4533
4538 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
4534 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
4539 'ignored' in show, 'clean' in show, 'unknown' in show,
4535 'ignored' in show, 'clean' in show, 'unknown' in show,
4540 opts.get('subrepos'))
4536 opts.get('subrepos'))
4541 changestates = zip(states, 'MAR!?IC', stat)
4537 changestates = zip(states, 'MAR!?IC', stat)
4542
4538
4543 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
4539 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
4544 ctxn = repo[nullid]
4540 ctxn = repo[nullid]
4545 ctx1 = repo[node1]
4541 ctx1 = repo[node1]
4546 ctx2 = repo[node2]
4542 ctx2 = repo[node2]
4547 added = stat[1]
4543 added = stat[1]
4548 if node2 is None:
4544 if node2 is None:
4549 added = stat[0] + stat[1] # merged?
4545 added = stat[0] + stat[1] # merged?
4550
4546
4551 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
4547 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
4552 if k in added:
4548 if k in added:
4553 copy[k] = v
4549 copy[k] = v
4554 elif v in added:
4550 elif v in added:
4555 copy[v] = k
4551 copy[v] = k
4556
4552
4557 for state, char, files in changestates:
4553 for state, char, files in changestates:
4558 if state in show:
4554 if state in show:
4559 format = "%s %%s%s" % (char, end)
4555 format = "%s %%s%s" % (char, end)
4560 if opts.get('no_status'):
4556 if opts.get('no_status'):
4561 format = "%%s%s" % end
4557 format = "%%s%s" % end
4562
4558
4563 for f in files:
4559 for f in files:
4564 ui.write(format % repo.pathto(f, cwd),
4560 ui.write(format % repo.pathto(f, cwd),
4565 label='status.' + state)
4561 label='status.' + state)
4566 if f in copy:
4562 if f in copy:
4567 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
4563 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
4568 label='status.copied')
4564 label='status.copied')
4569
4565
4570 @command('^summary|sum',
4566 @command('^summary|sum',
4571 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4567 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4572 def summary(ui, repo, **opts):
4568 def summary(ui, repo, **opts):
4573 """summarize working directory state
4569 """summarize working directory state
4574
4570
4575 This generates a brief summary of the working directory state,
4571 This generates a brief summary of the working directory state,
4576 including parents, branch, commit status, and available updates.
4572 including parents, branch, commit status, and available updates.
4577
4573
4578 With the --remote option, this will check the default paths for
4574 With the --remote option, this will check the default paths for
4579 incoming and outgoing changes. This can be time-consuming.
4575 incoming and outgoing changes. This can be time-consuming.
4580
4576
4581 Returns 0 on success.
4577 Returns 0 on success.
4582 """
4578 """
4583
4579
4584 ctx = repo[None]
4580 ctx = repo[None]
4585 parents = ctx.parents()
4581 parents = ctx.parents()
4586 pnode = parents[0].node()
4582 pnode = parents[0].node()
4587
4583
4588 for p in parents:
4584 for p in parents:
4589 # label with log.changeset (instead of log.parent) since this
4585 # label with log.changeset (instead of log.parent) since this
4590 # shows a working directory parent *changeset*:
4586 # shows a working directory parent *changeset*:
4591 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
4587 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
4592 label='log.changeset')
4588 label='log.changeset')
4593 ui.write(' '.join(p.tags()), label='log.tag')
4589 ui.write(' '.join(p.tags()), label='log.tag')
4594 if p.bookmarks():
4590 if p.bookmarks():
4595 ui.write(' ' + ' '.join(p.bookmarks()), label='log.bookmark')
4591 ui.write(' ' + ' '.join(p.bookmarks()), label='log.bookmark')
4596 if p.rev() == -1:
4592 if p.rev() == -1:
4597 if not len(repo):
4593 if not len(repo):
4598 ui.write(_(' (empty repository)'))
4594 ui.write(_(' (empty repository)'))
4599 else:
4595 else:
4600 ui.write(_(' (no revision checked out)'))
4596 ui.write(_(' (no revision checked out)'))
4601 ui.write('\n')
4597 ui.write('\n')
4602 if p.description():
4598 if p.description():
4603 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4599 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4604 label='log.summary')
4600 label='log.summary')
4605
4601
4606 branch = ctx.branch()
4602 branch = ctx.branch()
4607 bheads = repo.branchheads(branch)
4603 bheads = repo.branchheads(branch)
4608 m = _('branch: %s\n') % branch
4604 m = _('branch: %s\n') % branch
4609 if branch != 'default':
4605 if branch != 'default':
4610 ui.write(m, label='log.branch')
4606 ui.write(m, label='log.branch')
4611 else:
4607 else:
4612 ui.status(m, label='log.branch')
4608 ui.status(m, label='log.branch')
4613
4609
4614 st = list(repo.status(unknown=True))[:6]
4610 st = list(repo.status(unknown=True))[:6]
4615
4611
4616 c = repo.dirstate.copies()
4612 c = repo.dirstate.copies()
4617 copied, renamed = [], []
4613 copied, renamed = [], []
4618 for d, s in c.iteritems():
4614 for d, s in c.iteritems():
4619 if s in st[2]:
4615 if s in st[2]:
4620 st[2].remove(s)
4616 st[2].remove(s)
4621 renamed.append(d)
4617 renamed.append(d)
4622 else:
4618 else:
4623 copied.append(d)
4619 copied.append(d)
4624 if d in st[1]:
4620 if d in st[1]:
4625 st[1].remove(d)
4621 st[1].remove(d)
4626 st.insert(3, renamed)
4622 st.insert(3, renamed)
4627 st.insert(4, copied)
4623 st.insert(4, copied)
4628
4624
4629 ms = mergemod.mergestate(repo)
4625 ms = mergemod.mergestate(repo)
4630 st.append([f for f in ms if ms[f] == 'u'])
4626 st.append([f for f in ms if ms[f] == 'u'])
4631
4627
4632 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4628 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4633 st.append(subs)
4629 st.append(subs)
4634
4630
4635 labels = [ui.label(_('%d modified'), 'status.modified'),
4631 labels = [ui.label(_('%d modified'), 'status.modified'),
4636 ui.label(_('%d added'), 'status.added'),
4632 ui.label(_('%d added'), 'status.added'),
4637 ui.label(_('%d removed'), 'status.removed'),
4633 ui.label(_('%d removed'), 'status.removed'),
4638 ui.label(_('%d renamed'), 'status.copied'),
4634 ui.label(_('%d renamed'), 'status.copied'),
4639 ui.label(_('%d copied'), 'status.copied'),
4635 ui.label(_('%d copied'), 'status.copied'),
4640 ui.label(_('%d deleted'), 'status.deleted'),
4636 ui.label(_('%d deleted'), 'status.deleted'),
4641 ui.label(_('%d unknown'), 'status.unknown'),
4637 ui.label(_('%d unknown'), 'status.unknown'),
4642 ui.label(_('%d ignored'), 'status.ignored'),
4638 ui.label(_('%d ignored'), 'status.ignored'),
4643 ui.label(_('%d unresolved'), 'resolve.unresolved'),
4639 ui.label(_('%d unresolved'), 'resolve.unresolved'),
4644 ui.label(_('%d subrepos'), 'status.modified')]
4640 ui.label(_('%d subrepos'), 'status.modified')]
4645 t = []
4641 t = []
4646 for s, l in zip(st, labels):
4642 for s, l in zip(st, labels):
4647 if s:
4643 if s:
4648 t.append(l % len(s))
4644 t.append(l % len(s))
4649
4645
4650 t = ', '.join(t)
4646 t = ', '.join(t)
4651 cleanworkdir = False
4647 cleanworkdir = False
4652
4648
4653 if len(parents) > 1:
4649 if len(parents) > 1:
4654 t += _(' (merge)')
4650 t += _(' (merge)')
4655 elif branch != parents[0].branch():
4651 elif branch != parents[0].branch():
4656 t += _(' (new branch)')
4652 t += _(' (new branch)')
4657 elif (parents[0].extra().get('close') and
4653 elif (parents[0].extra().get('close') and
4658 pnode in repo.branchheads(branch, closed=True)):
4654 pnode in repo.branchheads(branch, closed=True)):
4659 t += _(' (head closed)')
4655 t += _(' (head closed)')
4660 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
4656 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
4661 t += _(' (clean)')
4657 t += _(' (clean)')
4662 cleanworkdir = True
4658 cleanworkdir = True
4663 elif pnode not in bheads:
4659 elif pnode not in bheads:
4664 t += _(' (new branch head)')
4660 t += _(' (new branch head)')
4665
4661
4666 if cleanworkdir:
4662 if cleanworkdir:
4667 ui.status(_('commit: %s\n') % t.strip())
4663 ui.status(_('commit: %s\n') % t.strip())
4668 else:
4664 else:
4669 ui.write(_('commit: %s\n') % t.strip())
4665 ui.write(_('commit: %s\n') % t.strip())
4670
4666
4671 # all ancestors of branch heads - all ancestors of parent = new csets
4667 # all ancestors of branch heads - all ancestors of parent = new csets
4672 new = [0] * len(repo)
4668 new = [0] * len(repo)
4673 cl = repo.changelog
4669 cl = repo.changelog
4674 for a in [cl.rev(n) for n in bheads]:
4670 for a in [cl.rev(n) for n in bheads]:
4675 new[a] = 1
4671 new[a] = 1
4676 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
4672 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
4677 new[a] = 1
4673 new[a] = 1
4678 for a in [p.rev() for p in parents]:
4674 for a in [p.rev() for p in parents]:
4679 if a >= 0:
4675 if a >= 0:
4680 new[a] = 0
4676 new[a] = 0
4681 for a in cl.ancestors(*[p.rev() for p in parents]):
4677 for a in cl.ancestors(*[p.rev() for p in parents]):
4682 new[a] = 0
4678 new[a] = 0
4683 new = sum(new)
4679 new = sum(new)
4684
4680
4685 if new == 0:
4681 if new == 0:
4686 ui.status(_('update: (current)\n'))
4682 ui.status(_('update: (current)\n'))
4687 elif pnode not in bheads:
4683 elif pnode not in bheads:
4688 ui.write(_('update: %d new changesets (update)\n') % new)
4684 ui.write(_('update: %d new changesets (update)\n') % new)
4689 else:
4685 else:
4690 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4686 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4691 (new, len(bheads)))
4687 (new, len(bheads)))
4692
4688
4693 if opts.get('remote'):
4689 if opts.get('remote'):
4694 t = []
4690 t = []
4695 source, branches = hg.parseurl(ui.expandpath('default'))
4691 source, branches = hg.parseurl(ui.expandpath('default'))
4696 other = hg.repository(hg.remoteui(repo, {}), source)
4692 other = hg.repository(hg.remoteui(repo, {}), source)
4697 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4693 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4698 ui.debug('comparing with %s\n' % util.hidepassword(source))
4694 ui.debug('comparing with %s\n' % util.hidepassword(source))
4699 repo.ui.pushbuffer()
4695 repo.ui.pushbuffer()
4700 commoninc = discovery.findcommonincoming(repo, other)
4696 commoninc = discovery.findcommonincoming(repo, other)
4701 _common, incoming, _rheads = commoninc
4697 _common, incoming, _rheads = commoninc
4702 repo.ui.popbuffer()
4698 repo.ui.popbuffer()
4703 if incoming:
4699 if incoming:
4704 t.append(_('1 or more incoming'))
4700 t.append(_('1 or more incoming'))
4705
4701
4706 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
4702 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
4707 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
4703 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
4708 if source != dest:
4704 if source != dest:
4709 other = hg.repository(hg.remoteui(repo, {}), dest)
4705 other = hg.repository(hg.remoteui(repo, {}), dest)
4710 commoninc = None
4706 commoninc = None
4711 ui.debug('comparing with %s\n' % util.hidepassword(dest))
4707 ui.debug('comparing with %s\n' % util.hidepassword(dest))
4712 repo.ui.pushbuffer()
4708 repo.ui.pushbuffer()
4713 common, outheads = discovery.findcommonoutgoing(repo, other,
4709 common, outheads = discovery.findcommonoutgoing(repo, other,
4714 commoninc=commoninc)
4710 commoninc=commoninc)
4715 repo.ui.popbuffer()
4711 repo.ui.popbuffer()
4716 o = repo.changelog.findmissing(common=common, heads=outheads)
4712 o = repo.changelog.findmissing(common=common, heads=outheads)
4717 if o:
4713 if o:
4718 t.append(_('%d outgoing') % len(o))
4714 t.append(_('%d outgoing') % len(o))
4719 if 'bookmarks' in other.listkeys('namespaces'):
4715 if 'bookmarks' in other.listkeys('namespaces'):
4720 lmarks = repo.listkeys('bookmarks')
4716 lmarks = repo.listkeys('bookmarks')
4721 rmarks = other.listkeys('bookmarks')
4717 rmarks = other.listkeys('bookmarks')
4722 diff = set(rmarks) - set(lmarks)
4718 diff = set(rmarks) - set(lmarks)
4723 if len(diff) > 0:
4719 if len(diff) > 0:
4724 t.append(_('%d incoming bookmarks') % len(diff))
4720 t.append(_('%d incoming bookmarks') % len(diff))
4725 diff = set(lmarks) - set(rmarks)
4721 diff = set(lmarks) - set(rmarks)
4726 if len(diff) > 0:
4722 if len(diff) > 0:
4727 t.append(_('%d outgoing bookmarks') % len(diff))
4723 t.append(_('%d outgoing bookmarks') % len(diff))
4728
4724
4729 if t:
4725 if t:
4730 ui.write(_('remote: %s\n') % (', '.join(t)))
4726 ui.write(_('remote: %s\n') % (', '.join(t)))
4731 else:
4727 else:
4732 ui.status(_('remote: (synced)\n'))
4728 ui.status(_('remote: (synced)\n'))
4733
4729
4734 @command('tag',
4730 @command('tag',
4735 [('f', 'force', None, _('force tag')),
4731 [('f', 'force', None, _('force tag')),
4736 ('l', 'local', None, _('make the tag local')),
4732 ('l', 'local', None, _('make the tag local')),
4737 ('r', 'rev', '', _('revision to tag'), _('REV')),
4733 ('r', 'rev', '', _('revision to tag'), _('REV')),
4738 ('', 'remove', None, _('remove a tag')),
4734 ('', 'remove', None, _('remove a tag')),
4739 # -l/--local is already there, commitopts cannot be used
4735 # -l/--local is already there, commitopts cannot be used
4740 ('e', 'edit', None, _('edit commit message')),
4736 ('e', 'edit', None, _('edit commit message')),
4741 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
4737 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
4742 ] + commitopts2,
4738 ] + commitopts2,
4743 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
4739 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
4744 def tag(ui, repo, name1, *names, **opts):
4740 def tag(ui, repo, name1, *names, **opts):
4745 """add one or more tags for the current or given revision
4741 """add one or more tags for the current or given revision
4746
4742
4747 Name a particular revision using <name>.
4743 Name a particular revision using <name>.
4748
4744
4749 Tags are used to name particular revisions of the repository and are
4745 Tags are used to name particular revisions of the repository and are
4750 very useful to compare different revisions, to go back to significant
4746 very useful to compare different revisions, to go back to significant
4751 earlier versions or to mark branch points as releases, etc. Changing
4747 earlier versions or to mark branch points as releases, etc. Changing
4752 an existing tag is normally disallowed; use -f/--force to override.
4748 an existing tag is normally disallowed; use -f/--force to override.
4753
4749
4754 If no revision is given, the parent of the working directory is
4750 If no revision is given, the parent of the working directory is
4755 used, or tip if no revision is checked out.
4751 used, or tip if no revision is checked out.
4756
4752
4757 To facilitate version control, distribution, and merging of tags,
4753 To facilitate version control, distribution, and merging of tags,
4758 they are stored as a file named ".hgtags" which is managed similarly
4754 they are stored as a file named ".hgtags" which is managed similarly
4759 to other project files and can be hand-edited if necessary. This
4755 to other project files and can be hand-edited if necessary. This
4760 also means that tagging creates a new commit. The file
4756 also means that tagging creates a new commit. The file
4761 ".hg/localtags" is used for local tags (not shared among
4757 ".hg/localtags" is used for local tags (not shared among
4762 repositories).
4758 repositories).
4763
4759
4764 Tag commits are usually made at the head of a branch. If the parent
4760 Tag commits are usually made at the head of a branch. If the parent
4765 of the working directory is not a branch head, :hg:`tag` aborts; use
4761 of the working directory is not a branch head, :hg:`tag` aborts; use
4766 -f/--force to force the tag commit to be based on a non-head
4762 -f/--force to force the tag commit to be based on a non-head
4767 changeset.
4763 changeset.
4768
4764
4769 See :hg:`help dates` for a list of formats valid for -d/--date.
4765 See :hg:`help dates` for a list of formats valid for -d/--date.
4770
4766
4771 Since tag names have priority over branch names during revision
4767 Since tag names have priority over branch names during revision
4772 lookup, using an existing branch name as a tag name is discouraged.
4768 lookup, using an existing branch name as a tag name is discouraged.
4773
4769
4774 Returns 0 on success.
4770 Returns 0 on success.
4775 """
4771 """
4776
4772
4777 rev_ = "."
4773 rev_ = "."
4778 names = [t.strip() for t in (name1,) + names]
4774 names = [t.strip() for t in (name1,) + names]
4779 if len(names) != len(set(names)):
4775 if len(names) != len(set(names)):
4780 raise util.Abort(_('tag names must be unique'))
4776 raise util.Abort(_('tag names must be unique'))
4781 for n in names:
4777 for n in names:
4782 if n in ['tip', '.', 'null']:
4778 if n in ['tip', '.', 'null']:
4783 raise util.Abort(_("the name '%s' is reserved") % n)
4779 raise util.Abort(_("the name '%s' is reserved") % n)
4784 if not n:
4780 if not n:
4785 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
4781 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
4786 if opts.get('rev') and opts.get('remove'):
4782 if opts.get('rev') and opts.get('remove'):
4787 raise util.Abort(_("--rev and --remove are incompatible"))
4783 raise util.Abort(_("--rev and --remove are incompatible"))
4788 if opts.get('rev'):
4784 if opts.get('rev'):
4789 rev_ = opts['rev']
4785 rev_ = opts['rev']
4790 message = opts.get('message')
4786 message = opts.get('message')
4791 if opts.get('remove'):
4787 if opts.get('remove'):
4792 expectedtype = opts.get('local') and 'local' or 'global'
4788 expectedtype = opts.get('local') and 'local' or 'global'
4793 for n in names:
4789 for n in names:
4794 if not repo.tagtype(n):
4790 if not repo.tagtype(n):
4795 raise util.Abort(_("tag '%s' does not exist") % n)
4791 raise util.Abort(_("tag '%s' does not exist") % n)
4796 if repo.tagtype(n) != expectedtype:
4792 if repo.tagtype(n) != expectedtype:
4797 if expectedtype == 'global':
4793 if expectedtype == 'global':
4798 raise util.Abort(_("tag '%s' is not a global tag") % n)
4794 raise util.Abort(_("tag '%s' is not a global tag") % n)
4799 else:
4795 else:
4800 raise util.Abort(_("tag '%s' is not a local tag") % n)
4796 raise util.Abort(_("tag '%s' is not a local tag") % n)
4801 rev_ = nullid
4797 rev_ = nullid
4802 if not message:
4798 if not message:
4803 # we don't translate commit messages
4799 # we don't translate commit messages
4804 message = 'Removed tag %s' % ', '.join(names)
4800 message = 'Removed tag %s' % ', '.join(names)
4805 elif not opts.get('force'):
4801 elif not opts.get('force'):
4806 for n in names:
4802 for n in names:
4807 if n in repo.tags():
4803 if n in repo.tags():
4808 raise util.Abort(_("tag '%s' already exists "
4804 raise util.Abort(_("tag '%s' already exists "
4809 "(use -f to force)") % n)
4805 "(use -f to force)") % n)
4810 if not opts.get('local'):
4806 if not opts.get('local'):
4811 p1, p2 = repo.dirstate.parents()
4807 p1, p2 = repo.dirstate.parents()
4812 if p2 != nullid:
4808 if p2 != nullid:
4813 raise util.Abort(_('uncommitted merge'))
4809 raise util.Abort(_('uncommitted merge'))
4814 bheads = repo.branchheads()
4810 bheads = repo.branchheads()
4815 if not opts.get('force') and bheads and p1 not in bheads:
4811 if not opts.get('force') and bheads and p1 not in bheads:
4816 raise util.Abort(_('not at a branch head (use -f to force)'))
4812 raise util.Abort(_('not at a branch head (use -f to force)'))
4817 r = cmdutil.revsingle(repo, rev_).node()
4813 r = cmdutil.revsingle(repo, rev_).node()
4818
4814
4819 if not message:
4815 if not message:
4820 # we don't translate commit messages
4816 # we don't translate commit messages
4821 message = ('Added tag %s for changeset %s' %
4817 message = ('Added tag %s for changeset %s' %
4822 (', '.join(names), short(r)))
4818 (', '.join(names), short(r)))
4823
4819
4824 date = opts.get('date')
4820 date = opts.get('date')
4825 if date:
4821 if date:
4826 date = util.parsedate(date)
4822 date = util.parsedate(date)
4827
4823
4828 if opts.get('edit'):
4824 if opts.get('edit'):
4829 message = ui.edit(message, ui.username())
4825 message = ui.edit(message, ui.username())
4830
4826
4831 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
4827 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
4832
4828
4833 @command('tags', [], '')
4829 @command('tags', [], '')
4834 def tags(ui, repo):
4830 def tags(ui, repo):
4835 """list repository tags
4831 """list repository tags
4836
4832
4837 This lists both regular and local tags. When the -v/--verbose
4833 This lists both regular and local tags. When the -v/--verbose
4838 switch is used, a third column "local" is printed for local tags.
4834 switch is used, a third column "local" is printed for local tags.
4839
4835
4840 Returns 0 on success.
4836 Returns 0 on success.
4841 """
4837 """
4842
4838
4843 hexfunc = ui.debugflag and hex or short
4839 hexfunc = ui.debugflag and hex or short
4844 tagtype = ""
4840 tagtype = ""
4845
4841
4846 for t, n in reversed(repo.tagslist()):
4842 for t, n in reversed(repo.tagslist()):
4847 if ui.quiet:
4843 if ui.quiet:
4848 ui.write("%s\n" % t)
4844 ui.write("%s\n" % t)
4849 continue
4845 continue
4850
4846
4851 hn = hexfunc(n)
4847 hn = hexfunc(n)
4852 r = "%5d:%s" % (repo.changelog.rev(n), hn)
4848 r = "%5d:%s" % (repo.changelog.rev(n), hn)
4853 spaces = " " * (30 - encoding.colwidth(t))
4849 spaces = " " * (30 - encoding.colwidth(t))
4854
4850
4855 if ui.verbose:
4851 if ui.verbose:
4856 if repo.tagtype(t) == 'local':
4852 if repo.tagtype(t) == 'local':
4857 tagtype = " local"
4853 tagtype = " local"
4858 else:
4854 else:
4859 tagtype = ""
4855 tagtype = ""
4860 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
4856 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
4861
4857
4862 @command('tip',
4858 @command('tip',
4863 [('p', 'patch', None, _('show patch')),
4859 [('p', 'patch', None, _('show patch')),
4864 ('g', 'git', None, _('use git extended diff format')),
4860 ('g', 'git', None, _('use git extended diff format')),
4865 ] + templateopts,
4861 ] + templateopts,
4866 _('[-p] [-g]'))
4862 _('[-p] [-g]'))
4867 def tip(ui, repo, **opts):
4863 def tip(ui, repo, **opts):
4868 """show the tip revision
4864 """show the tip revision
4869
4865
4870 The tip revision (usually just called the tip) is the changeset
4866 The tip revision (usually just called the tip) is the changeset
4871 most recently added to the repository (and therefore the most
4867 most recently added to the repository (and therefore the most
4872 recently changed head).
4868 recently changed head).
4873
4869
4874 If you have just made a commit, that commit will be the tip. If
4870 If you have just made a commit, that commit will be the tip. If
4875 you have just pulled changes from another repository, the tip of
4871 you have just pulled changes from another repository, the tip of
4876 that repository becomes the current tip. The "tip" tag is special
4872 that repository becomes the current tip. The "tip" tag is special
4877 and cannot be renamed or assigned to a different changeset.
4873 and cannot be renamed or assigned to a different changeset.
4878
4874
4879 Returns 0 on success.
4875 Returns 0 on success.
4880 """
4876 """
4881 displayer = cmdutil.show_changeset(ui, repo, opts)
4877 displayer = cmdutil.show_changeset(ui, repo, opts)
4882 displayer.show(repo[len(repo) - 1])
4878 displayer.show(repo[len(repo) - 1])
4883 displayer.close()
4879 displayer.close()
4884
4880
4885 @command('unbundle',
4881 @command('unbundle',
4886 [('u', 'update', None,
4882 [('u', 'update', None,
4887 _('update to new branch head if changesets were unbundled'))],
4883 _('update to new branch head if changesets were unbundled'))],
4888 _('[-u] FILE...'))
4884 _('[-u] FILE...'))
4889 def unbundle(ui, repo, fname1, *fnames, **opts):
4885 def unbundle(ui, repo, fname1, *fnames, **opts):
4890 """apply one or more changegroup files
4886 """apply one or more changegroup files
4891
4887
4892 Apply one or more compressed changegroup files generated by the
4888 Apply one or more compressed changegroup files generated by the
4893 bundle command.
4889 bundle command.
4894
4890
4895 Returns 0 on success, 1 if an update has unresolved files.
4891 Returns 0 on success, 1 if an update has unresolved files.
4896 """
4892 """
4897 fnames = (fname1,) + fnames
4893 fnames = (fname1,) + fnames
4898
4894
4899 lock = repo.lock()
4895 lock = repo.lock()
4900 wc = repo['.']
4896 wc = repo['.']
4901 try:
4897 try:
4902 for fname in fnames:
4898 for fname in fnames:
4903 f = url.open(ui, fname)
4899 f = url.open(ui, fname)
4904 gen = changegroup.readbundle(f, fname)
4900 gen = changegroup.readbundle(f, fname)
4905 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
4901 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
4906 lock=lock)
4902 lock=lock)
4907 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
4903 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
4908 finally:
4904 finally:
4909 lock.release()
4905 lock.release()
4910 return postincoming(ui, repo, modheads, opts.get('update'), None)
4906 return postincoming(ui, repo, modheads, opts.get('update'), None)
4911
4907
4912 @command('^update|up|checkout|co',
4908 @command('^update|up|checkout|co',
4913 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4909 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
4914 ('c', 'check', None,
4910 ('c', 'check', None,
4915 _('update across branches if no uncommitted changes')),
4911 _('update across branches if no uncommitted changes')),
4916 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4912 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4917 ('r', 'rev', '', _('revision'), _('REV'))],
4913 ('r', 'rev', '', _('revision'), _('REV'))],
4918 _('[-c] [-C] [-d DATE] [[-r] REV]'))
4914 _('[-c] [-C] [-d DATE] [[-r] REV]'))
4919 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
4915 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
4920 """update working directory (or switch revisions)
4916 """update working directory (or switch revisions)
4921
4917
4922 Update the repository's working directory to the specified
4918 Update the repository's working directory to the specified
4923 changeset. If no changeset is specified, update to the tip of the
4919 changeset. If no changeset is specified, update to the tip of the
4924 current named branch.
4920 current named branch.
4925
4921
4926 If the changeset is not a descendant of the working directory's
4922 If the changeset is not a descendant of the working directory's
4927 parent, the update is aborted. With the -c/--check option, the
4923 parent, the update is aborted. With the -c/--check option, the
4928 working directory is checked for uncommitted changes; if none are
4924 working directory is checked for uncommitted changes; if none are
4929 found, the working directory is updated to the specified
4925 found, the working directory is updated to the specified
4930 changeset.
4926 changeset.
4931
4927
4932 The following rules apply when the working directory contains
4928 The following rules apply when the working directory contains
4933 uncommitted changes:
4929 uncommitted changes:
4934
4930
4935 1. If neither -c/--check nor -C/--clean is specified, and if
4931 1. If neither -c/--check nor -C/--clean is specified, and if
4936 the requested changeset is an ancestor or descendant of
4932 the requested changeset is an ancestor or descendant of
4937 the working directory's parent, the uncommitted changes
4933 the working directory's parent, the uncommitted changes
4938 are merged into the requested changeset and the merged
4934 are merged into the requested changeset and the merged
4939 result is left uncommitted. If the requested changeset is
4935 result is left uncommitted. If the requested changeset is
4940 not an ancestor or descendant (that is, it is on another
4936 not an ancestor or descendant (that is, it is on another
4941 branch), the update is aborted and the uncommitted changes
4937 branch), the update is aborted and the uncommitted changes
4942 are preserved.
4938 are preserved.
4943
4939
4944 2. With the -c/--check option, the update is aborted and the
4940 2. With the -c/--check option, the update is aborted and the
4945 uncommitted changes are preserved.
4941 uncommitted changes are preserved.
4946
4942
4947 3. With the -C/--clean option, uncommitted changes are discarded and
4943 3. With the -C/--clean option, uncommitted changes are discarded and
4948 the working directory is updated to the requested changeset.
4944 the working directory is updated to the requested changeset.
4949
4945
4950 Use null as the changeset to remove the working directory (like
4946 Use null as the changeset to remove the working directory (like
4951 :hg:`clone -U`).
4947 :hg:`clone -U`).
4952
4948
4953 If you want to update just one file to an older changeset, use
4949 If you want to update just one file to an older changeset, use
4954 :hg:`revert`.
4950 :hg:`revert`.
4955
4951
4956 See :hg:`help dates` for a list of formats valid for -d/--date.
4952 See :hg:`help dates` for a list of formats valid for -d/--date.
4957
4953
4958 Returns 0 on success, 1 if there are unresolved files.
4954 Returns 0 on success, 1 if there are unresolved files.
4959 """
4955 """
4960 if rev and node:
4956 if rev and node:
4961 raise util.Abort(_("please specify just one revision"))
4957 raise util.Abort(_("please specify just one revision"))
4962
4958
4963 if rev is None or rev == '':
4959 if rev is None or rev == '':
4964 rev = node
4960 rev = node
4965
4961
4966 # if we defined a bookmark, we have to remember the original bookmark name
4962 # if we defined a bookmark, we have to remember the original bookmark name
4967 brev = rev
4963 brev = rev
4968 rev = cmdutil.revsingle(repo, rev, rev).rev()
4964 rev = cmdutil.revsingle(repo, rev, rev).rev()
4969
4965
4970 if check and clean:
4966 if check and clean:
4971 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
4967 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
4972
4968
4973 if check:
4969 if check:
4974 # we could use dirty() but we can ignore merge and branch trivia
4970 # we could use dirty() but we can ignore merge and branch trivia
4975 c = repo[None]
4971 c = repo[None]
4976 if c.modified() or c.added() or c.removed():
4972 if c.modified() or c.added() or c.removed():
4977 raise util.Abort(_("uncommitted local changes"))
4973 raise util.Abort(_("uncommitted local changes"))
4978
4974
4979 if date:
4975 if date:
4980 if rev is not None:
4976 if rev is not None:
4981 raise util.Abort(_("you can't specify a revision and a date"))
4977 raise util.Abort(_("you can't specify a revision and a date"))
4982 rev = cmdutil.finddate(ui, repo, date)
4978 rev = cmdutil.finddate(ui, repo, date)
4983
4979
4984 if clean or check:
4980 if clean or check:
4985 ret = hg.clean(repo, rev)
4981 ret = hg.clean(repo, rev)
4986 else:
4982 else:
4987 ret = hg.update(repo, rev)
4983 ret = hg.update(repo, rev)
4988
4984
4989 if brev in repo._bookmarks:
4985 if brev in repo._bookmarks:
4990 bookmarks.setcurrent(repo, brev)
4986 bookmarks.setcurrent(repo, brev)
4991
4987
4992 return ret
4988 return ret
4993
4989
4994 @command('verify', [])
4990 @command('verify', [])
4995 def verify(ui, repo):
4991 def verify(ui, repo):
4996 """verify the integrity of the repository
4992 """verify the integrity of the repository
4997
4993
4998 Verify the integrity of the current repository.
4994 Verify the integrity of the current repository.
4999
4995
5000 This will perform an extensive check of the repository's
4996 This will perform an extensive check of the repository's
5001 integrity, validating the hashes and checksums of each entry in
4997 integrity, validating the hashes and checksums of each entry in
5002 the changelog, manifest, and tracked files, as well as the
4998 the changelog, manifest, and tracked files, as well as the
5003 integrity of their crosslinks and indices.
4999 integrity of their crosslinks and indices.
5004
5000
5005 Returns 0 on success, 1 if errors are encountered.
5001 Returns 0 on success, 1 if errors are encountered.
5006 """
5002 """
5007 return hg.verify(repo)
5003 return hg.verify(repo)
5008
5004
5009 @command('version', [])
5005 @command('version', [])
5010 def version_(ui):
5006 def version_(ui):
5011 """output version and copyright information"""
5007 """output version and copyright information"""
5012 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5008 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5013 % util.version())
5009 % util.version())
5014 ui.status(_(
5010 ui.status(_(
5015 "(see http://mercurial.selenic.com for more information)\n"
5011 "(see http://mercurial.selenic.com for more information)\n"
5016 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
5012 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
5017 "This is free software; see the source for copying conditions. "
5013 "This is free software; see the source for copying conditions. "
5018 "There is NO\nwarranty; "
5014 "There is NO\nwarranty; "
5019 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5015 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5020 ))
5016 ))
5021
5017
5022 norepo = ("clone init version help debugcommands debugcomplete"
5018 norepo = ("clone init version help debugcommands debugcomplete"
5023 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5019 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5024 " debugknown debuggetbundle debugbundle")
5020 " debugknown debuggetbundle debugbundle")
5025 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5021 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5026 " debugdata debugindex debugindexdot")
5022 " debugdata debugindex debugindexdot")
@@ -1,334 +1,334 b''
1 # extensions.py - extension handling for mercurial
1 # extensions.py - extension handling 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 of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import imp, os
8 import imp, os
9 import util, cmdutil, error
9 import util, cmdutil, error
10 from i18n import _, gettext
10 from i18n import _, gettext
11
11
12 _extensions = {}
12 _extensions = {}
13 _order = []
13 _order = []
14 _ignore = ['hbisect', 'bookmarks', 'parentrevspec']
14 _ignore = ['hbisect', 'bookmarks', 'parentrevspec']
15
15
16 def extensions():
16 def extensions():
17 for name in _order:
17 for name in _order:
18 module = _extensions[name]
18 module = _extensions[name]
19 if module:
19 if module:
20 yield name, module
20 yield name, module
21
21
22 def find(name):
22 def find(name):
23 '''return module with given extension name'''
23 '''return module with given extension name'''
24 try:
24 try:
25 return _extensions[name]
25 return _extensions[name]
26 except KeyError:
26 except KeyError:
27 for k, v in _extensions.iteritems():
27 for k, v in _extensions.iteritems():
28 if k.endswith('.' + name) or k.endswith('/' + name):
28 if k.endswith('.' + name) or k.endswith('/' + name):
29 return v
29 return v
30 raise KeyError(name)
30 raise KeyError(name)
31
31
32 def loadpath(path, module_name):
32 def loadpath(path, module_name):
33 module_name = module_name.replace('.', '_')
33 module_name = module_name.replace('.', '_')
34 path = util.expandpath(path)
34 path = util.expandpath(path)
35 if os.path.isdir(path):
35 if os.path.isdir(path):
36 # module/__init__.py style
36 # module/__init__.py style
37 d, f = os.path.split(path.rstrip('/'))
37 d, f = os.path.split(path.rstrip('/'))
38 fd, fpath, desc = imp.find_module(f, [d])
38 fd, fpath, desc = imp.find_module(f, [d])
39 return imp.load_module(module_name, fd, fpath, desc)
39 return imp.load_module(module_name, fd, fpath, desc)
40 else:
40 else:
41 return imp.load_source(module_name, path)
41 return imp.load_source(module_name, path)
42
42
43 def load(ui, name, path):
43 def load(ui, name, path):
44 # unused ui argument kept for backwards compatibility
44 # unused ui argument kept for backwards compatibility
45 if name.startswith('hgext.') or name.startswith('hgext/'):
45 if name.startswith('hgext.') or name.startswith('hgext/'):
46 shortname = name[6:]
46 shortname = name[6:]
47 else:
47 else:
48 shortname = name
48 shortname = name
49 if shortname in _ignore:
49 if shortname in _ignore:
50 return None
50 return None
51 if shortname in _extensions:
51 if shortname in _extensions:
52 return _extensions[shortname]
52 return _extensions[shortname]
53 _extensions[shortname] = None
53 _extensions[shortname] = None
54 if path:
54 if path:
55 # the module will be loaded in sys.modules
55 # the module will be loaded in sys.modules
56 # choose an unique name so that it doesn't
56 # choose an unique name so that it doesn't
57 # conflicts with other modules
57 # conflicts with other modules
58 mod = loadpath(path, 'hgext.%s' % name)
58 mod = loadpath(path, 'hgext.%s' % name)
59 else:
59 else:
60 def importh(name):
60 def importh(name):
61 mod = __import__(name)
61 mod = __import__(name)
62 components = name.split('.')
62 components = name.split('.')
63 for comp in components[1:]:
63 for comp in components[1:]:
64 mod = getattr(mod, comp)
64 mod = getattr(mod, comp)
65 return mod
65 return mod
66 try:
66 try:
67 mod = importh("hgext.%s" % name)
67 mod = importh("hgext.%s" % name)
68 except ImportError:
68 except ImportError:
69 mod = importh(name)
69 mod = importh(name)
70 _extensions[shortname] = mod
70 _extensions[shortname] = mod
71 _order.append(shortname)
71 _order.append(shortname)
72 return mod
72 return mod
73
73
74 def loadall(ui):
74 def loadall(ui):
75 result = ui.configitems("extensions")
75 result = ui.configitems("extensions")
76 newindex = len(_order)
76 newindex = len(_order)
77 for (name, path) in result:
77 for (name, path) in result:
78 if path:
78 if path:
79 if path[0] == '!':
79 if path[0] == '!':
80 continue
80 continue
81 try:
81 try:
82 load(ui, name, path)
82 load(ui, name, path)
83 except KeyboardInterrupt:
83 except KeyboardInterrupt:
84 raise
84 raise
85 except Exception, inst:
85 except Exception, inst:
86 if path:
86 if path:
87 ui.warn(_("*** failed to import extension %s from %s: %s\n")
87 ui.warn(_("*** failed to import extension %s from %s: %s\n")
88 % (name, path, inst))
88 % (name, path, inst))
89 else:
89 else:
90 ui.warn(_("*** failed to import extension %s: %s\n")
90 ui.warn(_("*** failed to import extension %s: %s\n")
91 % (name, inst))
91 % (name, inst))
92 if ui.traceback():
92 if ui.traceback():
93 return 1
93 return 1
94
94
95 for name in _order[newindex:]:
95 for name in _order[newindex:]:
96 uisetup = getattr(_extensions[name], 'uisetup', None)
96 uisetup = getattr(_extensions[name], 'uisetup', None)
97 if uisetup:
97 if uisetup:
98 uisetup(ui)
98 uisetup(ui)
99
99
100 for name in _order[newindex:]:
100 for name in _order[newindex:]:
101 extsetup = getattr(_extensions[name], 'extsetup', None)
101 extsetup = getattr(_extensions[name], 'extsetup', None)
102 if extsetup:
102 if extsetup:
103 try:
103 try:
104 extsetup(ui)
104 extsetup(ui)
105 except TypeError:
105 except TypeError:
106 if extsetup.func_code.co_argcount != 0:
106 if extsetup.func_code.co_argcount != 0:
107 raise
107 raise
108 extsetup() # old extsetup with no ui argument
108 extsetup() # old extsetup with no ui argument
109
109
110 def wrapcommand(table, command, wrapper):
110 def wrapcommand(table, command, wrapper):
111 '''Wrap the command named `command' in table
111 '''Wrap the command named `command' in table
112
112
113 Replace command in the command table with wrapper. The wrapped command will
113 Replace command in the command table with wrapper. The wrapped command will
114 be inserted into the command table specified by the table argument.
114 be inserted into the command table specified by the table argument.
115
115
116 The wrapper will be called like
116 The wrapper will be called like
117
117
118 wrapper(orig, *args, **kwargs)
118 wrapper(orig, *args, **kwargs)
119
119
120 where orig is the original (wrapped) function, and *args, **kwargs
120 where orig is the original (wrapped) function, and *args, **kwargs
121 are the arguments passed to it.
121 are the arguments passed to it.
122 '''
122 '''
123 assert hasattr(wrapper, '__call__')
123 assert hasattr(wrapper, '__call__')
124 aliases, entry = cmdutil.findcmd(command, table)
124 aliases, entry = cmdutil.findcmd(command, table)
125 for alias, e in table.iteritems():
125 for alias, e in table.iteritems():
126 if e is entry:
126 if e is entry:
127 key = alias
127 key = alias
128 break
128 break
129
129
130 origfn = entry[0]
130 origfn = entry[0]
131 def wrap(*args, **kwargs):
131 def wrap(*args, **kwargs):
132 return util.checksignature(wrapper)(
132 return util.checksignature(wrapper)(
133 util.checksignature(origfn), *args, **kwargs)
133 util.checksignature(origfn), *args, **kwargs)
134
134
135 wrap.__doc__ = getattr(origfn, '__doc__')
135 wrap.__doc__ = getattr(origfn, '__doc__')
136 wrap.__module__ = getattr(origfn, '__module__')
136 wrap.__module__ = getattr(origfn, '__module__')
137
137
138 newentry = list(entry)
138 newentry = list(entry)
139 newentry[0] = wrap
139 newentry[0] = wrap
140 table[key] = tuple(newentry)
140 table[key] = tuple(newentry)
141 return entry
141 return entry
142
142
143 def wrapfunction(container, funcname, wrapper):
143 def wrapfunction(container, funcname, wrapper):
144 '''Wrap the function named funcname in container
144 '''Wrap the function named funcname in container
145
145
146 Replace the funcname member in the given container with the specified
146 Replace the funcname member in the given container with the specified
147 wrapper. The container is typically a module, class, or instance.
147 wrapper. The container is typically a module, class, or instance.
148
148
149 The wrapper will be called like
149 The wrapper will be called like
150
150
151 wrapper(orig, *args, **kwargs)
151 wrapper(orig, *args, **kwargs)
152
152
153 where orig is the original (wrapped) function, and *args, **kwargs
153 where orig is the original (wrapped) function, and *args, **kwargs
154 are the arguments passed to it.
154 are the arguments passed to it.
155
155
156 Wrapping methods of the repository object is not recommended since
156 Wrapping methods of the repository object is not recommended since
157 it conflicts with extensions that extend the repository by
157 it conflicts with extensions that extend the repository by
158 subclassing. All extensions that need to extend methods of
158 subclassing. All extensions that need to extend methods of
159 localrepository should use this subclassing trick: namely,
159 localrepository should use this subclassing trick: namely,
160 reposetup() should look like
160 reposetup() should look like
161
161
162 def reposetup(ui, repo):
162 def reposetup(ui, repo):
163 class myrepo(repo.__class__):
163 class myrepo(repo.__class__):
164 def whatever(self, *args, **kwargs):
164 def whatever(self, *args, **kwargs):
165 [...extension stuff...]
165 [...extension stuff...]
166 super(myrepo, self).whatever(*args, **kwargs)
166 super(myrepo, self).whatever(*args, **kwargs)
167 [...extension stuff...]
167 [...extension stuff...]
168
168
169 repo.__class__ = myrepo
169 repo.__class__ = myrepo
170
170
171 In general, combining wrapfunction() with subclassing does not
171 In general, combining wrapfunction() with subclassing does not
172 work. Since you cannot control what other extensions are loaded by
172 work. Since you cannot control what other extensions are loaded by
173 your end users, you should play nicely with others by using the
173 your end users, you should play nicely with others by using the
174 subclass trick.
174 subclass trick.
175 '''
175 '''
176 assert hasattr(wrapper, '__call__')
176 assert hasattr(wrapper, '__call__')
177 def wrap(*args, **kwargs):
177 def wrap(*args, **kwargs):
178 return wrapper(origfn, *args, **kwargs)
178 return wrapper(origfn, *args, **kwargs)
179
179
180 origfn = getattr(container, funcname)
180 origfn = getattr(container, funcname)
181 assert hasattr(origfn, '__call__')
181 assert hasattr(origfn, '__call__')
182 setattr(container, funcname, wrap)
182 setattr(container, funcname, wrap)
183 return origfn
183 return origfn
184
184
185 def _disabledpaths(strip_init=False):
185 def _disabledpaths(strip_init=False):
186 '''find paths of disabled extensions. returns a dict of {name: path}
186 '''find paths of disabled extensions. returns a dict of {name: path}
187 removes /__init__.py from packages if strip_init is True'''
187 removes /__init__.py from packages if strip_init is True'''
188 import hgext
188 import hgext
189 extpath = os.path.dirname(os.path.abspath(hgext.__file__))
189 extpath = os.path.dirname(os.path.abspath(hgext.__file__))
190 try: # might not be a filesystem path
190 try: # might not be a filesystem path
191 files = os.listdir(extpath)
191 files = os.listdir(extpath)
192 except OSError:
192 except OSError:
193 return {}
193 return {}
194
194
195 exts = {}
195 exts = {}
196 for e in files:
196 for e in files:
197 if e.endswith('.py'):
197 if e.endswith('.py'):
198 name = e.rsplit('.', 1)[0]
198 name = e.rsplit('.', 1)[0]
199 path = os.path.join(extpath, e)
199 path = os.path.join(extpath, e)
200 else:
200 else:
201 name = e
201 name = e
202 path = os.path.join(extpath, e, '__init__.py')
202 path = os.path.join(extpath, e, '__init__.py')
203 if not os.path.exists(path):
203 if not os.path.exists(path):
204 continue
204 continue
205 if strip_init:
205 if strip_init:
206 path = os.path.dirname(path)
206 path = os.path.dirname(path)
207 if name in exts or name in _order or name == '__init__':
207 if name in exts or name in _order or name == '__init__':
208 continue
208 continue
209 exts[name] = path
209 exts[name] = path
210 return exts
210 return exts
211
211
212 def _moduledoc(file):
212 def _moduledoc(file):
213 '''return the top-level python documentation for the given file
213 '''return the top-level python documentation for the given file
214
214
215 Loosely inspired by pydoc.source_synopsis(), but rewritten to
215 Loosely inspired by pydoc.source_synopsis(), but rewritten to
216 handle triple quotes and to return the whole text instead of just
216 handle triple quotes and to return the whole text instead of just
217 the synopsis'''
217 the synopsis'''
218 result = []
218 result = []
219
219
220 line = file.readline()
220 line = file.readline()
221 while line[:1] == '#' or not line.strip():
221 while line[:1] == '#' or not line.strip():
222 line = file.readline()
222 line = file.readline()
223 if not line:
223 if not line:
224 break
224 break
225
225
226 start = line[:3]
226 start = line[:3]
227 if start == '"""' or start == "'''":
227 if start == '"""' or start == "'''":
228 line = line[3:]
228 line = line[3:]
229 while line:
229 while line:
230 if line.rstrip().endswith(start):
230 if line.rstrip().endswith(start):
231 line = line.split(start)[0]
231 line = line.split(start)[0]
232 if line:
232 if line:
233 result.append(line)
233 result.append(line)
234 break
234 break
235 elif not line:
235 elif not line:
236 return None # unmatched delimiter
236 return None # unmatched delimiter
237 result.append(line)
237 result.append(line)
238 line = file.readline()
238 line = file.readline()
239 else:
239 else:
240 return None
240 return None
241
241
242 return ''.join(result)
242 return ''.join(result)
243
243
244 def _disabledhelp(path):
244 def _disabledhelp(path):
245 '''retrieve help synopsis of a disabled extension (without importing)'''
245 '''retrieve help synopsis of a disabled extension (without importing)'''
246 try:
246 try:
247 file = open(path)
247 file = open(path)
248 except IOError:
248 except IOError:
249 return
249 return
250 else:
250 else:
251 doc = moduledoc(file)
251 doc = _moduledoc(file)
252 file.close()
252 file.close()
253
253
254 if doc: # extracting localized synopsis
254 if doc: # extracting localized synopsis
255 return gettext(doc).splitlines()[0]
255 return gettext(doc).splitlines()[0]
256 else:
256 else:
257 return _('(no help text available)')
257 return _('(no help text available)')
258
258
259 def disabled():
259 def disabled():
260 '''find disabled extensions from hgext
260 '''find disabled extensions from hgext
261 returns a dict of {name: desc}, and the max name length'''
261 returns a dict of {name: desc}, and the max name length'''
262
262
263 paths = _disabledpaths()
263 paths = _disabledpaths()
264 if not paths:
264 if not paths:
265 return None
265 return None
266
266
267 exts = {}
267 exts = {}
268 for name, path in paths.iteritems():
268 for name, path in paths.iteritems():
269 doc = _disabledhelp(path)
269 doc = _disabledhelp(path)
270 if doc:
270 if doc:
271 exts[name] = doc
271 exts[name] = doc
272
272
273 return exts
273 return exts
274
274
275 def disabledext(name):
275 def disabledext(name):
276 '''find a specific disabled extension from hgext. returns desc'''
276 '''find a specific disabled extension from hgext. returns desc'''
277 paths = _disabledpaths()
277 paths = _disabledpaths()
278 if name in paths:
278 if name in paths:
279 return _disabledhelp(paths[name])
279 return _disabledhelp(paths[name])
280
280
281 def disabledcmd(ui, cmd, strict=False):
281 def disabledcmd(ui, cmd, strict=False):
282 '''import disabled extensions until cmd is found.
282 '''import disabled extensions until cmd is found.
283 returns (cmdname, extname, doc)'''
283 returns (cmdname, extname, doc)'''
284
284
285 paths = _disabledpaths(strip_init=True)
285 paths = _disabledpaths(strip_init=True)
286 if not paths:
286 if not paths:
287 raise error.UnknownCommand(cmd)
287 raise error.UnknownCommand(cmd)
288
288
289 def findcmd(cmd, name, path):
289 def findcmd(cmd, name, path):
290 try:
290 try:
291 mod = loadpath(path, 'hgext.%s' % name)
291 mod = loadpath(path, 'hgext.%s' % name)
292 except Exception:
292 except Exception:
293 return
293 return
294 try:
294 try:
295 aliases, entry = cmdutil.findcmd(cmd,
295 aliases, entry = cmdutil.findcmd(cmd,
296 getattr(mod, 'cmdtable', {}), strict)
296 getattr(mod, 'cmdtable', {}), strict)
297 except (error.AmbiguousCommand, error.UnknownCommand):
297 except (error.AmbiguousCommand, error.UnknownCommand):
298 return
298 return
299 except Exception:
299 except Exception:
300 ui.warn(_('warning: error finding commands in %s\n') % path)
300 ui.warn(_('warning: error finding commands in %s\n') % path)
301 ui.traceback()
301 ui.traceback()
302 return
302 return
303 for c in aliases:
303 for c in aliases:
304 if c.startswith(cmd):
304 if c.startswith(cmd):
305 cmd = c
305 cmd = c
306 break
306 break
307 else:
307 else:
308 cmd = aliases[0]
308 cmd = aliases[0]
309 return (cmd, name, mod)
309 return (cmd, name, mod)
310
310
311 # first, search for an extension with the same name as the command
311 # first, search for an extension with the same name as the command
312 path = paths.pop(cmd, None)
312 path = paths.pop(cmd, None)
313 if path:
313 if path:
314 ext = findcmd(cmd, cmd, path)
314 ext = findcmd(cmd, cmd, path)
315 if ext:
315 if ext:
316 return ext
316 return ext
317
317
318 # otherwise, interrogate each extension until there's a match
318 # otherwise, interrogate each extension until there's a match
319 for name, path in paths.iteritems():
319 for name, path in paths.iteritems():
320 ext = findcmd(cmd, name, path)
320 ext = findcmd(cmd, name, path)
321 if ext:
321 if ext:
322 return ext
322 return ext
323
323
324 raise error.UnknownCommand(cmd)
324 raise error.UnknownCommand(cmd)
325
325
326 def enabled():
326 def enabled():
327 '''return a dict of {name: desc} of extensions, and the max name length'''
327 '''return a dict of {name: desc} of extensions, and the max name length'''
328 exts = {}
328 exts = {}
329 for ename, ext in extensions():
329 for ename, ext in extensions():
330 doc = (gettext(ext.__doc__) or _('(no help text available)'))
330 doc = (gettext(ext.__doc__) or _('(no help text available)'))
331 ename = ename.split('.')[-1]
331 ename = ename.split('.')[-1]
332 exts[ename] = doc.splitlines()[0].strip()
332 exts[ename] = doc.splitlines()[0].strip()
333
333
334 return exts
334 return exts
@@ -1,99 +1,107 b''
1 # help.py - help data for mercurial
1 # help.py - help data for mercurial
2 #
2 #
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import gettext, _
8 from i18n import gettext, _
9 import sys, os
9 import sys, os
10 import extensions
10 import extensions, revset, templatekw, templatefilters
11 import util
11 import util
12
12
13 def listexts(header, exts, indent=1):
13 def listexts(header, exts, indent=1):
14 '''return a text listing of the given extensions'''
14 '''return a text listing of the given extensions'''
15 if not exts:
15 if not exts:
16 return ''
16 return ''
17 maxlength = max(len(e) for e in exts)
17 maxlength = max(len(e) for e in exts)
18 result = '\n%s\n\n' % header
18 result = '\n%s\n\n' % header
19 for name, desc in sorted(exts.iteritems()):
19 for name, desc in sorted(exts.iteritems()):
20 result += '%s%-*s %s\n' % (' ' * indent, maxlength + 2,
20 result += '%s%-*s %s\n' % (' ' * indent, maxlength + 2,
21 ':%s:' % name, desc)
21 ':%s:' % name, desc)
22 return result
22 return result
23
23
24 def extshelp():
24 def extshelp():
25 doc = loaddoc('extensions')()
25 doc = loaddoc('extensions')()
26 doc += listexts(_('enabled extensions:'), extensions.enabled())
26 doc += listexts(_('enabled extensions:'), extensions.enabled())
27 doc += listexts(_('disabled extensions:'), extensions.disabled())
27 doc += listexts(_('disabled extensions:'), extensions.disabled())
28 return doc
28 return doc
29
29
30 def loaddoc(topic):
30 def loaddoc(topic):
31 """Return a delayed loader for help/topic.txt."""
31 """Return a delayed loader for help/topic.txt."""
32
32
33 def loader():
33 def loader():
34 if hasattr(sys, 'frozen'):
34 if hasattr(sys, 'frozen'):
35 module = sys.executable
35 module = sys.executable
36 else:
36 else:
37 module = __file__
37 module = __file__
38 base = os.path.dirname(module)
38 base = os.path.dirname(module)
39
39
40 for dir in ('.', '..'):
40 for dir in ('.', '..'):
41 docdir = os.path.join(base, dir, 'help')
41 docdir = os.path.join(base, dir, 'help')
42 if os.path.isdir(docdir):
42 if os.path.isdir(docdir):
43 break
43 break
44
44
45 path = os.path.join(docdir, topic + ".txt")
45 path = os.path.join(docdir, topic + ".txt")
46 doc = gettext(util.readfile(path))
46 doc = gettext(util.readfile(path))
47 for rewriter in helphooks.get(topic, []):
47 for rewriter in helphooks.get(topic, []):
48 doc = rewriter(topic, doc)
48 doc = rewriter(topic, doc)
49 return doc
49 return doc
50
50
51 return loader
51 return loader
52
52
53 helptable = sorted([
53 helptable = sorted([
54 (["config", "hgrc"], _("Configuration Files"), loaddoc('config')),
54 (["config", "hgrc"], _("Configuration Files"), loaddoc('config')),
55 (["dates"], _("Date Formats"), loaddoc('dates')),
55 (["dates"], _("Date Formats"), loaddoc('dates')),
56 (["patterns"], _("File Name Patterns"), loaddoc('patterns')),
56 (["patterns"], _("File Name Patterns"), loaddoc('patterns')),
57 (['environment', 'env'], _('Environment Variables'),
57 (['environment', 'env'], _('Environment Variables'),
58 loaddoc('environment')),
58 loaddoc('environment')),
59 (['revs', 'revisions'], _('Specifying Single Revisions'),
59 (['revs', 'revisions'], _('Specifying Single Revisions'),
60 loaddoc('revisions')),
60 loaddoc('revisions')),
61 (['mrevs', 'multirevs'], _('Specifying Multiple Revisions'),
61 (['mrevs', 'multirevs'], _('Specifying Multiple Revisions'),
62 loaddoc('multirevs')),
62 loaddoc('multirevs')),
63 (['revset', 'revsets'], _("Specifying Revision Sets"), loaddoc('revsets')),
63 (['revset', 'revsets'], _("Specifying Revision Sets"), loaddoc('revsets')),
64 (['diffs'], _('Diff Formats'), loaddoc('diffs')),
64 (['diffs'], _('Diff Formats'), loaddoc('diffs')),
65 (['merge-tools'], _('Merge Tools'), loaddoc('merge-tools')),
65 (['merge-tools'], _('Merge Tools'), loaddoc('merge-tools')),
66 (['templating', 'templates'], _('Template Usage'),
66 (['templating', 'templates'], _('Template Usage'),
67 loaddoc('templates')),
67 loaddoc('templates')),
68 (['urls'], _('URL Paths'), loaddoc('urls')),
68 (['urls'], _('URL Paths'), loaddoc('urls')),
69 (["extensions"], _("Using additional features"), extshelp),
69 (["extensions"], _("Using additional features"), extshelp),
70 (["subrepo", "subrepos"], _("Subrepositories"), loaddoc('subrepos')),
70 (["subrepo", "subrepos"], _("Subrepositories"), loaddoc('subrepos')),
71 (["hgweb"], _("Configuring hgweb"), loaddoc('hgweb')),
71 (["hgweb"], _("Configuring hgweb"), loaddoc('hgweb')),
72 (["glossary"], _("Glossary"), loaddoc('glossary')),
72 (["glossary"], _("Glossary"), loaddoc('glossary')),
73 (["hgignore", "ignore"], _("syntax for Mercurial ignore files"),
73 (["hgignore", "ignore"], _("syntax for Mercurial ignore files"),
74 loaddoc('hgignore')),
74 loaddoc('hgignore')),
75 ])
75 ])
76
76
77 # Map topics to lists of callable taking the current topic help and
77 # Map topics to lists of callable taking the current topic help and
78 # returning the updated version
78 # returning the updated version
79 helphooks = {
79 helphooks = {}
80 }
81
80
82 def addtopichook(topic, rewriter):
81 def addtopichook(topic, rewriter):
83 helphooks.setdefault(topic, []).append(rewriter)
82 helphooks.setdefault(topic, []).append(rewriter)
84
83
85 def makeitemsdoc(topic, doc, marker, items):
84 def makeitemsdoc(topic, doc, marker, items):
86 """Extract docstring from the items key to function mapping, build a
85 """Extract docstring from the items key to function mapping, build a
87 .single documentation block and use it to overwrite the marker in doc
86 .single documentation block and use it to overwrite the marker in doc
88 """
87 """
89 entries = []
88 entries = []
90 for name in sorted(items):
89 for name in sorted(items):
91 text = (items[name].__doc__ or '').rstrip()
90 text = (items[name].__doc__ or '').rstrip()
92 if not text:
91 if not text:
93 continue
92 continue
94 text = gettext(text)
93 text = gettext(text)
95 lines = text.splitlines()
94 lines = text.splitlines()
96 lines[1:] = [(' ' + l.strip()) for l in lines[1:]]
95 lines[1:] = [(' ' + l.strip()) for l in lines[1:]]
97 entries.append('\n'.join(lines))
96 entries.append('\n'.join(lines))
98 entries = '\n\n'.join(entries)
97 entries = '\n\n'.join(entries)
99 return doc.replace(marker, entries)
98 return doc.replace(marker, entries)
99
100 def addtopicsymbols(topic, marker, symbols):
101 def add(topic, doc):
102 return makeitemsdoc(topic, doc, marker, symbols)
103 addtopichook(topic, add)
104
105 addtopicsymbols('revsets', '.. predicatesmarker', revset.symbols)
106 addtopicsymbols('templates', '.. keywordsmarker', templatekw.keywords)
107 addtopicsymbols('templates', '.. filtersmarker', templatefilters.filters)
@@ -1,988 +1,985 b''
1 # revset.py - revision set queries for mercurial
1 # revset.py - revision set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import re
8 import re
9 import parser, util, error, discovery, help, hbisect
9 import parser, util, error, discovery, hbisect
10 import bookmarks as bookmarksmod
10 import bookmarks as bookmarksmod
11 import match as matchmod
11 import match as matchmod
12 from i18n import _
12 from i18n import _
13
13
14 elements = {
14 elements = {
15 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
15 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
16 "~": (18, None, ("ancestor", 18)),
16 "~": (18, None, ("ancestor", 18)),
17 "^": (18, None, ("parent", 18), ("parentpost", 18)),
17 "^": (18, None, ("parent", 18), ("parentpost", 18)),
18 "-": (5, ("negate", 19), ("minus", 5)),
18 "-": (5, ("negate", 19), ("minus", 5)),
19 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
19 "::": (17, ("dagrangepre", 17), ("dagrange", 17),
20 ("dagrangepost", 17)),
20 ("dagrangepost", 17)),
21 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
21 "..": (17, ("dagrangepre", 17), ("dagrange", 17),
22 ("dagrangepost", 17)),
22 ("dagrangepost", 17)),
23 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
23 ":": (15, ("rangepre", 15), ("range", 15), ("rangepost", 15)),
24 "not": (10, ("not", 10)),
24 "not": (10, ("not", 10)),
25 "!": (10, ("not", 10)),
25 "!": (10, ("not", 10)),
26 "and": (5, None, ("and", 5)),
26 "and": (5, None, ("and", 5)),
27 "&": (5, None, ("and", 5)),
27 "&": (5, None, ("and", 5)),
28 "or": (4, None, ("or", 4)),
28 "or": (4, None, ("or", 4)),
29 "|": (4, None, ("or", 4)),
29 "|": (4, None, ("or", 4)),
30 "+": (4, None, ("or", 4)),
30 "+": (4, None, ("or", 4)),
31 ",": (2, None, ("list", 2)),
31 ",": (2, None, ("list", 2)),
32 ")": (0, None, None),
32 ")": (0, None, None),
33 "symbol": (0, ("symbol",), None),
33 "symbol": (0, ("symbol",), None),
34 "string": (0, ("string",), None),
34 "string": (0, ("string",), None),
35 "end": (0, None, None),
35 "end": (0, None, None),
36 }
36 }
37
37
38 keywords = set(['and', 'or', 'not'])
38 keywords = set(['and', 'or', 'not'])
39
39
40 def tokenize(program):
40 def tokenize(program):
41 pos, l = 0, len(program)
41 pos, l = 0, len(program)
42 while pos < l:
42 while pos < l:
43 c = program[pos]
43 c = program[pos]
44 if c.isspace(): # skip inter-token whitespace
44 if c.isspace(): # skip inter-token whitespace
45 pass
45 pass
46 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
46 elif c == ':' and program[pos:pos + 2] == '::': # look ahead carefully
47 yield ('::', None, pos)
47 yield ('::', None, pos)
48 pos += 1 # skip ahead
48 pos += 1 # skip ahead
49 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
49 elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully
50 yield ('..', None, pos)
50 yield ('..', None, pos)
51 pos += 1 # skip ahead
51 pos += 1 # skip ahead
52 elif c in "():,-|&+!~^": # handle simple operators
52 elif c in "():,-|&+!~^": # handle simple operators
53 yield (c, None, pos)
53 yield (c, None, pos)
54 elif (c in '"\'' or c == 'r' and
54 elif (c in '"\'' or c == 'r' and
55 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
55 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
56 if c == 'r':
56 if c == 'r':
57 pos += 1
57 pos += 1
58 c = program[pos]
58 c = program[pos]
59 decode = lambda x: x
59 decode = lambda x: x
60 else:
60 else:
61 decode = lambda x: x.decode('string-escape')
61 decode = lambda x: x.decode('string-escape')
62 pos += 1
62 pos += 1
63 s = pos
63 s = pos
64 while pos < l: # find closing quote
64 while pos < l: # find closing quote
65 d = program[pos]
65 d = program[pos]
66 if d == '\\': # skip over escaped characters
66 if d == '\\': # skip over escaped characters
67 pos += 2
67 pos += 2
68 continue
68 continue
69 if d == c:
69 if d == c:
70 yield ('string', decode(program[s:pos]), s)
70 yield ('string', decode(program[s:pos]), s)
71 break
71 break
72 pos += 1
72 pos += 1
73 else:
73 else:
74 raise error.ParseError(_("unterminated string"), s)
74 raise error.ParseError(_("unterminated string"), s)
75 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
75 elif c.isalnum() or c in '._' or ord(c) > 127: # gather up a symbol/keyword
76 s = pos
76 s = pos
77 pos += 1
77 pos += 1
78 while pos < l: # find end of symbol
78 while pos < l: # find end of symbol
79 d = program[pos]
79 d = program[pos]
80 if not (d.isalnum() or d in "._" or ord(d) > 127):
80 if not (d.isalnum() or d in "._" or ord(d) > 127):
81 break
81 break
82 if d == '.' and program[pos - 1] == '.': # special case for ..
82 if d == '.' and program[pos - 1] == '.': # special case for ..
83 pos -= 1
83 pos -= 1
84 break
84 break
85 pos += 1
85 pos += 1
86 sym = program[s:pos]
86 sym = program[s:pos]
87 if sym in keywords: # operator keywords
87 if sym in keywords: # operator keywords
88 yield (sym, None, s)
88 yield (sym, None, s)
89 else:
89 else:
90 yield ('symbol', sym, s)
90 yield ('symbol', sym, s)
91 pos -= 1
91 pos -= 1
92 else:
92 else:
93 raise error.ParseError(_("syntax error"), pos)
93 raise error.ParseError(_("syntax error"), pos)
94 pos += 1
94 pos += 1
95 yield ('end', None, pos)
95 yield ('end', None, pos)
96
96
97 # helpers
97 # helpers
98
98
99 def getstring(x, err):
99 def getstring(x, err):
100 if x and (x[0] == 'string' or x[0] == 'symbol'):
100 if x and (x[0] == 'string' or x[0] == 'symbol'):
101 return x[1]
101 return x[1]
102 raise error.ParseError(err)
102 raise error.ParseError(err)
103
103
104 def getlist(x):
104 def getlist(x):
105 if not x:
105 if not x:
106 return []
106 return []
107 if x[0] == 'list':
107 if x[0] == 'list':
108 return getlist(x[1]) + [x[2]]
108 return getlist(x[1]) + [x[2]]
109 return [x]
109 return [x]
110
110
111 def getargs(x, min, max, err):
111 def getargs(x, min, max, err):
112 l = getlist(x)
112 l = getlist(x)
113 if len(l) < min or len(l) > max:
113 if len(l) < min or len(l) > max:
114 raise error.ParseError(err)
114 raise error.ParseError(err)
115 return l
115 return l
116
116
117 def getset(repo, subset, x):
117 def getset(repo, subset, x):
118 if not x:
118 if not x:
119 raise error.ParseError(_("missing argument"))
119 raise error.ParseError(_("missing argument"))
120 return methods[x[0]](repo, subset, *x[1:])
120 return methods[x[0]](repo, subset, *x[1:])
121
121
122 # operator methods
122 # operator methods
123
123
124 def stringset(repo, subset, x):
124 def stringset(repo, subset, x):
125 x = repo[x].rev()
125 x = repo[x].rev()
126 if x == -1 and len(subset) == len(repo):
126 if x == -1 and len(subset) == len(repo):
127 return [-1]
127 return [-1]
128 if len(subset) == len(repo) or x in subset:
128 if len(subset) == len(repo) or x in subset:
129 return [x]
129 return [x]
130 return []
130 return []
131
131
132 def symbolset(repo, subset, x):
132 def symbolset(repo, subset, x):
133 if x in symbols:
133 if x in symbols:
134 raise error.ParseError(_("can't use %s here") % x)
134 raise error.ParseError(_("can't use %s here") % x)
135 return stringset(repo, subset, x)
135 return stringset(repo, subset, x)
136
136
137 def rangeset(repo, subset, x, y):
137 def rangeset(repo, subset, x, y):
138 m = getset(repo, subset, x)
138 m = getset(repo, subset, x)
139 if not m:
139 if not m:
140 m = getset(repo, range(len(repo)), x)
140 m = getset(repo, range(len(repo)), x)
141
141
142 n = getset(repo, subset, y)
142 n = getset(repo, subset, y)
143 if not n:
143 if not n:
144 n = getset(repo, range(len(repo)), y)
144 n = getset(repo, range(len(repo)), y)
145
145
146 if not m or not n:
146 if not m or not n:
147 return []
147 return []
148 m, n = m[0], n[-1]
148 m, n = m[0], n[-1]
149
149
150 if m < n:
150 if m < n:
151 r = range(m, n + 1)
151 r = range(m, n + 1)
152 else:
152 else:
153 r = range(m, n - 1, -1)
153 r = range(m, n - 1, -1)
154 s = set(subset)
154 s = set(subset)
155 return [x for x in r if x in s]
155 return [x for x in r if x in s]
156
156
157 def andset(repo, subset, x, y):
157 def andset(repo, subset, x, y):
158 return getset(repo, getset(repo, subset, x), y)
158 return getset(repo, getset(repo, subset, x), y)
159
159
160 def orset(repo, subset, x, y):
160 def orset(repo, subset, x, y):
161 xl = getset(repo, subset, x)
161 xl = getset(repo, subset, x)
162 s = set(xl)
162 s = set(xl)
163 yl = getset(repo, [r for r in subset if r not in s], y)
163 yl = getset(repo, [r for r in subset if r not in s], y)
164 return xl + yl
164 return xl + yl
165
165
166 def notset(repo, subset, x):
166 def notset(repo, subset, x):
167 s = set(getset(repo, subset, x))
167 s = set(getset(repo, subset, x))
168 return [r for r in subset if r not in s]
168 return [r for r in subset if r not in s]
169
169
170 def listset(repo, subset, a, b):
170 def listset(repo, subset, a, b):
171 raise error.ParseError(_("can't use a list in this context"))
171 raise error.ParseError(_("can't use a list in this context"))
172
172
173 def func(repo, subset, a, b):
173 def func(repo, subset, a, b):
174 if a[0] == 'symbol' and a[1] in symbols:
174 if a[0] == 'symbol' and a[1] in symbols:
175 return symbols[a[1]](repo, subset, b)
175 return symbols[a[1]](repo, subset, b)
176 raise error.ParseError(_("not a function: %s") % a[1])
176 raise error.ParseError(_("not a function: %s") % a[1])
177
177
178 # functions
178 # functions
179
179
180 def adds(repo, subset, x):
180 def adds(repo, subset, x):
181 """``adds(pattern)``
181 """``adds(pattern)``
182 Changesets that add a file matching pattern.
182 Changesets that add a file matching pattern.
183 """
183 """
184 # i18n: "adds" is a keyword
184 # i18n: "adds" is a keyword
185 pat = getstring(x, _("adds requires a pattern"))
185 pat = getstring(x, _("adds requires a pattern"))
186 return checkstatus(repo, subset, pat, 1)
186 return checkstatus(repo, subset, pat, 1)
187
187
188 def ancestor(repo, subset, x):
188 def ancestor(repo, subset, x):
189 """``ancestor(single, single)``
189 """``ancestor(single, single)``
190 Greatest common ancestor of the two changesets.
190 Greatest common ancestor of the two changesets.
191 """
191 """
192 # i18n: "ancestor" is a keyword
192 # i18n: "ancestor" is a keyword
193 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
193 l = getargs(x, 2, 2, _("ancestor requires two arguments"))
194 r = range(len(repo))
194 r = range(len(repo))
195 a = getset(repo, r, l[0])
195 a = getset(repo, r, l[0])
196 b = getset(repo, r, l[1])
196 b = getset(repo, r, l[1])
197 if len(a) != 1 or len(b) != 1:
197 if len(a) != 1 or len(b) != 1:
198 # i18n: "ancestor" is a keyword
198 # i18n: "ancestor" is a keyword
199 raise error.ParseError(_("ancestor arguments must be single revisions"))
199 raise error.ParseError(_("ancestor arguments must be single revisions"))
200 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
200 an = [repo[a[0]].ancestor(repo[b[0]]).rev()]
201
201
202 return [r for r in an if r in subset]
202 return [r for r in an if r in subset]
203
203
204 def ancestors(repo, subset, x):
204 def ancestors(repo, subset, x):
205 """``ancestors(set)``
205 """``ancestors(set)``
206 Changesets that are ancestors of a changeset in set.
206 Changesets that are ancestors of a changeset in set.
207 """
207 """
208 args = getset(repo, range(len(repo)), x)
208 args = getset(repo, range(len(repo)), x)
209 if not args:
209 if not args:
210 return []
210 return []
211 s = set(repo.changelog.ancestors(*args)) | set(args)
211 s = set(repo.changelog.ancestors(*args)) | set(args)
212 return [r for r in subset if r in s]
212 return [r for r in subset if r in s]
213
213
214 def ancestorspec(repo, subset, x, n):
214 def ancestorspec(repo, subset, x, n):
215 """``set~n``
215 """``set~n``
216 Changesets that are the Nth ancestor (first parents only) of a changeset in set.
216 Changesets that are the Nth ancestor (first parents only) of a changeset in set.
217 """
217 """
218 try:
218 try:
219 n = int(n[1])
219 n = int(n[1])
220 except ValueError:
220 except ValueError:
221 raise error.ParseError(_("~ expects a number"))
221 raise error.ParseError(_("~ expects a number"))
222 ps = set()
222 ps = set()
223 cl = repo.changelog
223 cl = repo.changelog
224 for r in getset(repo, subset, x):
224 for r in getset(repo, subset, x):
225 for i in range(n):
225 for i in range(n):
226 r = cl.parentrevs(r)[0]
226 r = cl.parentrevs(r)[0]
227 ps.add(r)
227 ps.add(r)
228 return [r for r in subset if r in ps]
228 return [r for r in subset if r in ps]
229
229
230 def author(repo, subset, x):
230 def author(repo, subset, x):
231 """``author(string)``
231 """``author(string)``
232 Alias for ``user(string)``.
232 Alias for ``user(string)``.
233 """
233 """
234 # i18n: "author" is a keyword
234 # i18n: "author" is a keyword
235 n = getstring(x, _("author requires a string")).lower()
235 n = getstring(x, _("author requires a string")).lower()
236 return [r for r in subset if n in repo[r].user().lower()]
236 return [r for r in subset if n in repo[r].user().lower()]
237
237
238 def bisected(repo, subset, x):
238 def bisected(repo, subset, x):
239 """``bisected(string)``
239 """``bisected(string)``
240 Changesets marked in the specified bisect state (good, bad, skip).
240 Changesets marked in the specified bisect state (good, bad, skip).
241 """
241 """
242 state = getstring(x, _("bisect requires a string")).lower()
242 state = getstring(x, _("bisect requires a string")).lower()
243 if state not in ('good', 'bad', 'skip', 'unknown'):
243 if state not in ('good', 'bad', 'skip', 'unknown'):
244 raise error.ParseError(_('invalid bisect state'))
244 raise error.ParseError(_('invalid bisect state'))
245 marked = set(repo.changelog.rev(n) for n in hbisect.load_state(repo)[state])
245 marked = set(repo.changelog.rev(n) for n in hbisect.load_state(repo)[state])
246 return [r for r in subset if r in marked]
246 return [r for r in subset if r in marked]
247
247
248 def bookmark(repo, subset, x):
248 def bookmark(repo, subset, x):
249 """``bookmark([name])``
249 """``bookmark([name])``
250 The named bookmark or all bookmarks.
250 The named bookmark or all bookmarks.
251 """
251 """
252 # i18n: "bookmark" is a keyword
252 # i18n: "bookmark" is a keyword
253 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
253 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
254 if args:
254 if args:
255 bm = getstring(args[0],
255 bm = getstring(args[0],
256 # i18n: "bookmark" is a keyword
256 # i18n: "bookmark" is a keyword
257 _('the argument to bookmark must be a string'))
257 _('the argument to bookmark must be a string'))
258 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
258 bmrev = bookmarksmod.listbookmarks(repo).get(bm, None)
259 if not bmrev:
259 if not bmrev:
260 raise util.Abort(_("bookmark '%s' does not exist") % bm)
260 raise util.Abort(_("bookmark '%s' does not exist") % bm)
261 bmrev = repo[bmrev].rev()
261 bmrev = repo[bmrev].rev()
262 return [r for r in subset if r == bmrev]
262 return [r for r in subset if r == bmrev]
263 bms = set([repo[r].rev()
263 bms = set([repo[r].rev()
264 for r in bookmarksmod.listbookmarks(repo).values()])
264 for r in bookmarksmod.listbookmarks(repo).values()])
265 return [r for r in subset if r in bms]
265 return [r for r in subset if r in bms]
266
266
267 def branch(repo, subset, x):
267 def branch(repo, subset, x):
268 """``branch(string or set)``
268 """``branch(string or set)``
269 All changesets belonging to the given branch or the branches of the given
269 All changesets belonging to the given branch or the branches of the given
270 changesets.
270 changesets.
271 """
271 """
272 try:
272 try:
273 b = getstring(x, '')
273 b = getstring(x, '')
274 if b in repo.branchmap():
274 if b in repo.branchmap():
275 return [r for r in subset if repo[r].branch() == b]
275 return [r for r in subset if repo[r].branch() == b]
276 except error.ParseError:
276 except error.ParseError:
277 # not a string, but another revspec, e.g. tip()
277 # not a string, but another revspec, e.g. tip()
278 pass
278 pass
279
279
280 s = getset(repo, range(len(repo)), x)
280 s = getset(repo, range(len(repo)), x)
281 b = set()
281 b = set()
282 for r in s:
282 for r in s:
283 b.add(repo[r].branch())
283 b.add(repo[r].branch())
284 s = set(s)
284 s = set(s)
285 return [r for r in subset if r in s or repo[r].branch() in b]
285 return [r for r in subset if r in s or repo[r].branch() in b]
286
286
287 def checkstatus(repo, subset, pat, field):
287 def checkstatus(repo, subset, pat, field):
288 m = matchmod.match(repo.root, repo.getcwd(), [pat])
288 m = matchmod.match(repo.root, repo.getcwd(), [pat])
289 s = []
289 s = []
290 fast = (m.files() == [pat])
290 fast = (m.files() == [pat])
291 for r in subset:
291 for r in subset:
292 c = repo[r]
292 c = repo[r]
293 if fast:
293 if fast:
294 if pat not in c.files():
294 if pat not in c.files():
295 continue
295 continue
296 else:
296 else:
297 for f in c.files():
297 for f in c.files():
298 if m(f):
298 if m(f):
299 break
299 break
300 else:
300 else:
301 continue
301 continue
302 files = repo.status(c.p1().node(), c.node())[field]
302 files = repo.status(c.p1().node(), c.node())[field]
303 if fast:
303 if fast:
304 if pat in files:
304 if pat in files:
305 s.append(r)
305 s.append(r)
306 else:
306 else:
307 for f in files:
307 for f in files:
308 if m(f):
308 if m(f):
309 s.append(r)
309 s.append(r)
310 break
310 break
311 return s
311 return s
312
312
313 def children(repo, subset, x):
313 def children(repo, subset, x):
314 """``children(set)``
314 """``children(set)``
315 Child changesets of changesets in set.
315 Child changesets of changesets in set.
316 """
316 """
317 cs = set()
317 cs = set()
318 cl = repo.changelog
318 cl = repo.changelog
319 s = set(getset(repo, range(len(repo)), x))
319 s = set(getset(repo, range(len(repo)), x))
320 for r in xrange(0, len(repo)):
320 for r in xrange(0, len(repo)):
321 for p in cl.parentrevs(r):
321 for p in cl.parentrevs(r):
322 if p in s:
322 if p in s:
323 cs.add(r)
323 cs.add(r)
324 return [r for r in subset if r in cs]
324 return [r for r in subset if r in cs]
325
325
326 def closed(repo, subset, x):
326 def closed(repo, subset, x):
327 """``closed()``
327 """``closed()``
328 Changeset is closed.
328 Changeset is closed.
329 """
329 """
330 # i18n: "closed" is a keyword
330 # i18n: "closed" is a keyword
331 getargs(x, 0, 0, _("closed takes no arguments"))
331 getargs(x, 0, 0, _("closed takes no arguments"))
332 return [r for r in subset if repo[r].extra().get('close')]
332 return [r for r in subset if repo[r].extra().get('close')]
333
333
334 def contains(repo, subset, x):
334 def contains(repo, subset, x):
335 """``contains(pattern)``
335 """``contains(pattern)``
336 Revision contains pattern.
336 Revision contains pattern.
337 """
337 """
338 # i18n: "contains" is a keyword
338 # i18n: "contains" is a keyword
339 pat = getstring(x, _("contains requires a pattern"))
339 pat = getstring(x, _("contains requires a pattern"))
340 m = matchmod.match(repo.root, repo.getcwd(), [pat])
340 m = matchmod.match(repo.root, repo.getcwd(), [pat])
341 s = []
341 s = []
342 if m.files() == [pat]:
342 if m.files() == [pat]:
343 for r in subset:
343 for r in subset:
344 if pat in repo[r]:
344 if pat in repo[r]:
345 s.append(r)
345 s.append(r)
346 else:
346 else:
347 for r in subset:
347 for r in subset:
348 for f in repo[r].manifest():
348 for f in repo[r].manifest():
349 if m(f):
349 if m(f):
350 s.append(r)
350 s.append(r)
351 break
351 break
352 return s
352 return s
353
353
354 def date(repo, subset, x):
354 def date(repo, subset, x):
355 """``date(interval)``
355 """``date(interval)``
356 Changesets within the interval, see :hg:`help dates`.
356 Changesets within the interval, see :hg:`help dates`.
357 """
357 """
358 # i18n: "date" is a keyword
358 # i18n: "date" is a keyword
359 ds = getstring(x, _("date requires a string"))
359 ds = getstring(x, _("date requires a string"))
360 dm = util.matchdate(ds)
360 dm = util.matchdate(ds)
361 return [r for r in subset if dm(repo[r].date()[0])]
361 return [r for r in subset if dm(repo[r].date()[0])]
362
362
363 def descendants(repo, subset, x):
363 def descendants(repo, subset, x):
364 """``descendants(set)``
364 """``descendants(set)``
365 Changesets which are descendants of changesets in set.
365 Changesets which are descendants of changesets in set.
366 """
366 """
367 args = getset(repo, range(len(repo)), x)
367 args = getset(repo, range(len(repo)), x)
368 if not args:
368 if not args:
369 return []
369 return []
370 s = set(repo.changelog.descendants(*args)) | set(args)
370 s = set(repo.changelog.descendants(*args)) | set(args)
371 return [r for r in subset if r in s]
371 return [r for r in subset if r in s]
372
372
373 def follow(repo, subset, x):
373 def follow(repo, subset, x):
374 """``follow()``
374 """``follow()``
375 An alias for ``::.`` (ancestors of the working copy's first parent).
375 An alias for ``::.`` (ancestors of the working copy's first parent).
376 """
376 """
377 # i18n: "follow" is a keyword
377 # i18n: "follow" is a keyword
378 getargs(x, 0, 0, _("follow takes no arguments"))
378 getargs(x, 0, 0, _("follow takes no arguments"))
379 p = repo['.'].rev()
379 p = repo['.'].rev()
380 s = set(repo.changelog.ancestors(p)) | set([p])
380 s = set(repo.changelog.ancestors(p)) | set([p])
381 return [r for r in subset if r in s]
381 return [r for r in subset if r in s]
382
382
383 def getall(repo, subset, x):
383 def getall(repo, subset, x):
384 """``all()``
384 """``all()``
385 All changesets, the same as ``0:tip``.
385 All changesets, the same as ``0:tip``.
386 """
386 """
387 # i18n: "all" is a keyword
387 # i18n: "all" is a keyword
388 getargs(x, 0, 0, _("all takes no arguments"))
388 getargs(x, 0, 0, _("all takes no arguments"))
389 return subset
389 return subset
390
390
391 def grep(repo, subset, x):
391 def grep(repo, subset, x):
392 """``grep(regex)``
392 """``grep(regex)``
393 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
393 Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
394 to ensure special escape characters are handled correctly.
394 to ensure special escape characters are handled correctly.
395 """
395 """
396 try:
396 try:
397 # i18n: "grep" is a keyword
397 # i18n: "grep" is a keyword
398 gr = re.compile(getstring(x, _("grep requires a string")))
398 gr = re.compile(getstring(x, _("grep requires a string")))
399 except re.error, e:
399 except re.error, e:
400 raise error.ParseError(_('invalid match pattern: %s') % e)
400 raise error.ParseError(_('invalid match pattern: %s') % e)
401 l = []
401 l = []
402 for r in subset:
402 for r in subset:
403 c = repo[r]
403 c = repo[r]
404 for e in c.files() + [c.user(), c.description()]:
404 for e in c.files() + [c.user(), c.description()]:
405 if gr.search(e):
405 if gr.search(e):
406 l.append(r)
406 l.append(r)
407 break
407 break
408 return l
408 return l
409
409
410 def hasfile(repo, subset, x):
410 def hasfile(repo, subset, x):
411 """``file(pattern)``
411 """``file(pattern)``
412 Changesets affecting files matched by pattern.
412 Changesets affecting files matched by pattern.
413 """
413 """
414 # i18n: "file" is a keyword
414 # i18n: "file" is a keyword
415 pat = getstring(x, _("file requires a pattern"))
415 pat = getstring(x, _("file requires a pattern"))
416 m = matchmod.match(repo.root, repo.getcwd(), [pat])
416 m = matchmod.match(repo.root, repo.getcwd(), [pat])
417 s = []
417 s = []
418 for r in subset:
418 for r in subset:
419 for f in repo[r].files():
419 for f in repo[r].files():
420 if m(f):
420 if m(f):
421 s.append(r)
421 s.append(r)
422 break
422 break
423 return s
423 return s
424
424
425 def head(repo, subset, x):
425 def head(repo, subset, x):
426 """``head()``
426 """``head()``
427 Changeset is a named branch head.
427 Changeset is a named branch head.
428 """
428 """
429 # i18n: "head" is a keyword
429 # i18n: "head" is a keyword
430 getargs(x, 0, 0, _("head takes no arguments"))
430 getargs(x, 0, 0, _("head takes no arguments"))
431 hs = set()
431 hs = set()
432 for b, ls in repo.branchmap().iteritems():
432 for b, ls in repo.branchmap().iteritems():
433 hs.update(repo[h].rev() for h in ls)
433 hs.update(repo[h].rev() for h in ls)
434 return [r for r in subset if r in hs]
434 return [r for r in subset if r in hs]
435
435
436 def heads(repo, subset, x):
436 def heads(repo, subset, x):
437 """``heads(set)``
437 """``heads(set)``
438 Members of set with no children in set.
438 Members of set with no children in set.
439 """
439 """
440 s = getset(repo, subset, x)
440 s = getset(repo, subset, x)
441 ps = set(parents(repo, subset, x))
441 ps = set(parents(repo, subset, x))
442 return [r for r in s if r not in ps]
442 return [r for r in s if r not in ps]
443
443
444 def keyword(repo, subset, x):
444 def keyword(repo, subset, x):
445 """``keyword(string)``
445 """``keyword(string)``
446 Search commit message, user name, and names of changed files for
446 Search commit message, user name, and names of changed files for
447 string.
447 string.
448 """
448 """
449 # i18n: "keyword" is a keyword
449 # i18n: "keyword" is a keyword
450 kw = getstring(x, _("keyword requires a string")).lower()
450 kw = getstring(x, _("keyword requires a string")).lower()
451 l = []
451 l = []
452 for r in subset:
452 for r in subset:
453 c = repo[r]
453 c = repo[r]
454 t = " ".join(c.files() + [c.user(), c.description()])
454 t = " ".join(c.files() + [c.user(), c.description()])
455 if kw in t.lower():
455 if kw in t.lower():
456 l.append(r)
456 l.append(r)
457 return l
457 return l
458
458
459 def limit(repo, subset, x):
459 def limit(repo, subset, x):
460 """``limit(set, n)``
460 """``limit(set, n)``
461 First n members of set.
461 First n members of set.
462 """
462 """
463 # i18n: "limit" is a keyword
463 # i18n: "limit" is a keyword
464 l = getargs(x, 2, 2, _("limit requires two arguments"))
464 l = getargs(x, 2, 2, _("limit requires two arguments"))
465 try:
465 try:
466 # i18n: "limit" is a keyword
466 # i18n: "limit" is a keyword
467 lim = int(getstring(l[1], _("limit requires a number")))
467 lim = int(getstring(l[1], _("limit requires a number")))
468 except ValueError:
468 except ValueError:
469 # i18n: "limit" is a keyword
469 # i18n: "limit" is a keyword
470 raise error.ParseError(_("limit expects a number"))
470 raise error.ParseError(_("limit expects a number"))
471 ss = set(subset)
471 ss = set(subset)
472 os = getset(repo, range(len(repo)), l[0])[:lim]
472 os = getset(repo, range(len(repo)), l[0])[:lim]
473 return [r for r in os if r in ss]
473 return [r for r in os if r in ss]
474
474
475 def last(repo, subset, x):
475 def last(repo, subset, x):
476 """``last(set, n)``
476 """``last(set, n)``
477 Last n members of set.
477 Last n members of set.
478 """
478 """
479 # i18n: "last" is a keyword
479 # i18n: "last" is a keyword
480 l = getargs(x, 2, 2, _("last requires two arguments"))
480 l = getargs(x, 2, 2, _("last requires two arguments"))
481 try:
481 try:
482 # i18n: "last" is a keyword
482 # i18n: "last" is a keyword
483 lim = int(getstring(l[1], _("last requires a number")))
483 lim = int(getstring(l[1], _("last requires a number")))
484 except ValueError:
484 except ValueError:
485 # i18n: "last" is a keyword
485 # i18n: "last" is a keyword
486 raise error.ParseError(_("last expects a number"))
486 raise error.ParseError(_("last expects a number"))
487 ss = set(subset)
487 ss = set(subset)
488 os = getset(repo, range(len(repo)), l[0])[-lim:]
488 os = getset(repo, range(len(repo)), l[0])[-lim:]
489 return [r for r in os if r in ss]
489 return [r for r in os if r in ss]
490
490
491 def maxrev(repo, subset, x):
491 def maxrev(repo, subset, x):
492 """``max(set)``
492 """``max(set)``
493 Changeset with highest revision number in set.
493 Changeset with highest revision number in set.
494 """
494 """
495 os = getset(repo, range(len(repo)), x)
495 os = getset(repo, range(len(repo)), x)
496 if os:
496 if os:
497 m = max(os)
497 m = max(os)
498 if m in subset:
498 if m in subset:
499 return [m]
499 return [m]
500 return []
500 return []
501
501
502 def merge(repo, subset, x):
502 def merge(repo, subset, x):
503 """``merge()``
503 """``merge()``
504 Changeset is a merge changeset.
504 Changeset is a merge changeset.
505 """
505 """
506 # i18n: "merge" is a keyword
506 # i18n: "merge" is a keyword
507 getargs(x, 0, 0, _("merge takes no arguments"))
507 getargs(x, 0, 0, _("merge takes no arguments"))
508 cl = repo.changelog
508 cl = repo.changelog
509 return [r for r in subset if cl.parentrevs(r)[1] != -1]
509 return [r for r in subset if cl.parentrevs(r)[1] != -1]
510
510
511 def minrev(repo, subset, x):
511 def minrev(repo, subset, x):
512 """``min(set)``
512 """``min(set)``
513 Changeset with lowest revision number in set.
513 Changeset with lowest revision number in set.
514 """
514 """
515 os = getset(repo, range(len(repo)), x)
515 os = getset(repo, range(len(repo)), x)
516 if os:
516 if os:
517 m = min(os)
517 m = min(os)
518 if m in subset:
518 if m in subset:
519 return [m]
519 return [m]
520 return []
520 return []
521
521
522 def modifies(repo, subset, x):
522 def modifies(repo, subset, x):
523 """``modifies(pattern)``
523 """``modifies(pattern)``
524 Changesets modifying files matched by pattern.
524 Changesets modifying files matched by pattern.
525 """
525 """
526 # i18n: "modifies" is a keyword
526 # i18n: "modifies" is a keyword
527 pat = getstring(x, _("modifies requires a pattern"))
527 pat = getstring(x, _("modifies requires a pattern"))
528 return checkstatus(repo, subset, pat, 0)
528 return checkstatus(repo, subset, pat, 0)
529
529
530 def node(repo, subset, x):
530 def node(repo, subset, x):
531 """``id(string)``
531 """``id(string)``
532 Revision non-ambiguously specified by the given hex string prefix.
532 Revision non-ambiguously specified by the given hex string prefix.
533 """
533 """
534 # i18n: "id" is a keyword
534 # i18n: "id" is a keyword
535 l = getargs(x, 1, 1, _("id requires one argument"))
535 l = getargs(x, 1, 1, _("id requires one argument"))
536 # i18n: "id" is a keyword
536 # i18n: "id" is a keyword
537 n = getstring(l[0], _("id requires a string"))
537 n = getstring(l[0], _("id requires a string"))
538 if len(n) == 40:
538 if len(n) == 40:
539 rn = repo[n].rev()
539 rn = repo[n].rev()
540 else:
540 else:
541 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
541 rn = repo.changelog.rev(repo.changelog._partialmatch(n))
542 return [r for r in subset if r == rn]
542 return [r for r in subset if r == rn]
543
543
544 def outgoing(repo, subset, x):
544 def outgoing(repo, subset, x):
545 """``outgoing([path])``
545 """``outgoing([path])``
546 Changesets not found in the specified destination repository, or the
546 Changesets not found in the specified destination repository, or the
547 default push location.
547 default push location.
548 """
548 """
549 import hg # avoid start-up nasties
549 import hg # avoid start-up nasties
550 # i18n: "outgoing" is a keyword
550 # i18n: "outgoing" is a keyword
551 l = getargs(x, 0, 1, _("outgoing requires a repository path"))
551 l = getargs(x, 0, 1, _("outgoing requires a repository path"))
552 # i18n: "outgoing" is a keyword
552 # i18n: "outgoing" is a keyword
553 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
553 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
554 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
554 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
555 dest, branches = hg.parseurl(dest)
555 dest, branches = hg.parseurl(dest)
556 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
556 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
557 if revs:
557 if revs:
558 revs = [repo.lookup(rev) for rev in revs]
558 revs = [repo.lookup(rev) for rev in revs]
559 other = hg.repository(hg.remoteui(repo, {}), dest)
559 other = hg.repository(hg.remoteui(repo, {}), dest)
560 repo.ui.pushbuffer()
560 repo.ui.pushbuffer()
561 common, outheads = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
561 common, outheads = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
562 repo.ui.popbuffer()
562 repo.ui.popbuffer()
563 cl = repo.changelog
563 cl = repo.changelog
564 o = set([cl.rev(r) for r in repo.changelog.findmissing(common, outheads)])
564 o = set([cl.rev(r) for r in repo.changelog.findmissing(common, outheads)])
565 return [r for r in subset if r in o]
565 return [r for r in subset if r in o]
566
566
567 def p1(repo, subset, x):
567 def p1(repo, subset, x):
568 """``p1([set])``
568 """``p1([set])``
569 First parent of changesets in set, or the working directory.
569 First parent of changesets in set, or the working directory.
570 """
570 """
571 if x is None:
571 if x is None:
572 p = repo[x].p1().rev()
572 p = repo[x].p1().rev()
573 return [r for r in subset if r == p]
573 return [r for r in subset if r == p]
574
574
575 ps = set()
575 ps = set()
576 cl = repo.changelog
576 cl = repo.changelog
577 for r in getset(repo, range(len(repo)), x):
577 for r in getset(repo, range(len(repo)), x):
578 ps.add(cl.parentrevs(r)[0])
578 ps.add(cl.parentrevs(r)[0])
579 return [r for r in subset if r in ps]
579 return [r for r in subset if r in ps]
580
580
581 def p2(repo, subset, x):
581 def p2(repo, subset, x):
582 """``p2([set])``
582 """``p2([set])``
583 Second parent of changesets in set, or the working directory.
583 Second parent of changesets in set, or the working directory.
584 """
584 """
585 if x is None:
585 if x is None:
586 ps = repo[x].parents()
586 ps = repo[x].parents()
587 try:
587 try:
588 p = ps[1].rev()
588 p = ps[1].rev()
589 return [r for r in subset if r == p]
589 return [r for r in subset if r == p]
590 except IndexError:
590 except IndexError:
591 return []
591 return []
592
592
593 ps = set()
593 ps = set()
594 cl = repo.changelog
594 cl = repo.changelog
595 for r in getset(repo, range(len(repo)), x):
595 for r in getset(repo, range(len(repo)), x):
596 ps.add(cl.parentrevs(r)[1])
596 ps.add(cl.parentrevs(r)[1])
597 return [r for r in subset if r in ps]
597 return [r for r in subset if r in ps]
598
598
599 def parents(repo, subset, x):
599 def parents(repo, subset, x):
600 """``parents([set])``
600 """``parents([set])``
601 The set of all parents for all changesets in set, or the working directory.
601 The set of all parents for all changesets in set, or the working directory.
602 """
602 """
603 if x is None:
603 if x is None:
604 ps = tuple(p.rev() for p in repo[x].parents())
604 ps = tuple(p.rev() for p in repo[x].parents())
605 return [r for r in subset if r in ps]
605 return [r for r in subset if r in ps]
606
606
607 ps = set()
607 ps = set()
608 cl = repo.changelog
608 cl = repo.changelog
609 for r in getset(repo, range(len(repo)), x):
609 for r in getset(repo, range(len(repo)), x):
610 ps.update(cl.parentrevs(r))
610 ps.update(cl.parentrevs(r))
611 return [r for r in subset if r in ps]
611 return [r for r in subset if r in ps]
612
612
613 def parentspec(repo, subset, x, n):
613 def parentspec(repo, subset, x, n):
614 """``set^0``
614 """``set^0``
615 The set.
615 The set.
616 ``set^1`` (or ``set^``), ``set^2``
616 ``set^1`` (or ``set^``), ``set^2``
617 First or second parent, respectively, of all changesets in set.
617 First or second parent, respectively, of all changesets in set.
618 """
618 """
619 try:
619 try:
620 n = int(n[1])
620 n = int(n[1])
621 if n not in (0, 1, 2):
621 if n not in (0, 1, 2):
622 raise ValueError
622 raise ValueError
623 except ValueError:
623 except ValueError:
624 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
624 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
625 ps = set()
625 ps = set()
626 cl = repo.changelog
626 cl = repo.changelog
627 for r in getset(repo, subset, x):
627 for r in getset(repo, subset, x):
628 if n == 0:
628 if n == 0:
629 ps.add(r)
629 ps.add(r)
630 elif n == 1:
630 elif n == 1:
631 ps.add(cl.parentrevs(r)[0])
631 ps.add(cl.parentrevs(r)[0])
632 elif n == 2:
632 elif n == 2:
633 parents = cl.parentrevs(r)
633 parents = cl.parentrevs(r)
634 if len(parents) > 1:
634 if len(parents) > 1:
635 ps.add(parents[1])
635 ps.add(parents[1])
636 return [r for r in subset if r in ps]
636 return [r for r in subset if r in ps]
637
637
638 def present(repo, subset, x):
638 def present(repo, subset, x):
639 """``present(set)``
639 """``present(set)``
640 An empty set, if any revision in set isn't found; otherwise,
640 An empty set, if any revision in set isn't found; otherwise,
641 all revisions in set.
641 all revisions in set.
642 """
642 """
643 try:
643 try:
644 return getset(repo, subset, x)
644 return getset(repo, subset, x)
645 except error.RepoLookupError:
645 except error.RepoLookupError:
646 return []
646 return []
647
647
648 def removes(repo, subset, x):
648 def removes(repo, subset, x):
649 """``removes(pattern)``
649 """``removes(pattern)``
650 Changesets which remove files matching pattern.
650 Changesets which remove files matching pattern.
651 """
651 """
652 # i18n: "removes" is a keyword
652 # i18n: "removes" is a keyword
653 pat = getstring(x, _("removes requires a pattern"))
653 pat = getstring(x, _("removes requires a pattern"))
654 return checkstatus(repo, subset, pat, 2)
654 return checkstatus(repo, subset, pat, 2)
655
655
656 def rev(repo, subset, x):
656 def rev(repo, subset, x):
657 """``rev(number)``
657 """``rev(number)``
658 Revision with the given numeric identifier.
658 Revision with the given numeric identifier.
659 """
659 """
660 # i18n: "rev" is a keyword
660 # i18n: "rev" is a keyword
661 l = getargs(x, 1, 1, _("rev requires one argument"))
661 l = getargs(x, 1, 1, _("rev requires one argument"))
662 try:
662 try:
663 # i18n: "rev" is a keyword
663 # i18n: "rev" is a keyword
664 l = int(getstring(l[0], _("rev requires a number")))
664 l = int(getstring(l[0], _("rev requires a number")))
665 except ValueError:
665 except ValueError:
666 # i18n: "rev" is a keyword
666 # i18n: "rev" is a keyword
667 raise error.ParseError(_("rev expects a number"))
667 raise error.ParseError(_("rev expects a number"))
668 return [r for r in subset if r == l]
668 return [r for r in subset if r == l]
669
669
670 def reverse(repo, subset, x):
670 def reverse(repo, subset, x):
671 """``reverse(set)``
671 """``reverse(set)``
672 Reverse order of set.
672 Reverse order of set.
673 """
673 """
674 l = getset(repo, subset, x)
674 l = getset(repo, subset, x)
675 l.reverse()
675 l.reverse()
676 return l
676 return l
677
677
678 def roots(repo, subset, x):
678 def roots(repo, subset, x):
679 """``roots(set)``
679 """``roots(set)``
680 Changesets with no parent changeset in set.
680 Changesets with no parent changeset in set.
681 """
681 """
682 s = getset(repo, subset, x)
682 s = getset(repo, subset, x)
683 cs = set(children(repo, subset, x))
683 cs = set(children(repo, subset, x))
684 return [r for r in s if r not in cs]
684 return [r for r in s if r not in cs]
685
685
686 def sort(repo, subset, x):
686 def sort(repo, subset, x):
687 """``sort(set[, [-]key...])``
687 """``sort(set[, [-]key...])``
688 Sort set by keys. The default sort order is ascending, specify a key
688 Sort set by keys. The default sort order is ascending, specify a key
689 as ``-key`` to sort in descending order.
689 as ``-key`` to sort in descending order.
690
690
691 The keys can be:
691 The keys can be:
692
692
693 - ``rev`` for the revision number,
693 - ``rev`` for the revision number,
694 - ``branch`` for the branch name,
694 - ``branch`` for the branch name,
695 - ``desc`` for the commit message (description),
695 - ``desc`` for the commit message (description),
696 - ``user`` for user name (``author`` can be used as an alias),
696 - ``user`` for user name (``author`` can be used as an alias),
697 - ``date`` for the commit date
697 - ``date`` for the commit date
698 """
698 """
699 # i18n: "sort" is a keyword
699 # i18n: "sort" is a keyword
700 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
700 l = getargs(x, 1, 2, _("sort requires one or two arguments"))
701 keys = "rev"
701 keys = "rev"
702 if len(l) == 2:
702 if len(l) == 2:
703 keys = getstring(l[1], _("sort spec must be a string"))
703 keys = getstring(l[1], _("sort spec must be a string"))
704
704
705 s = l[0]
705 s = l[0]
706 keys = keys.split()
706 keys = keys.split()
707 l = []
707 l = []
708 def invert(s):
708 def invert(s):
709 return "".join(chr(255 - ord(c)) for c in s)
709 return "".join(chr(255 - ord(c)) for c in s)
710 for r in getset(repo, subset, s):
710 for r in getset(repo, subset, s):
711 c = repo[r]
711 c = repo[r]
712 e = []
712 e = []
713 for k in keys:
713 for k in keys:
714 if k == 'rev':
714 if k == 'rev':
715 e.append(r)
715 e.append(r)
716 elif k == '-rev':
716 elif k == '-rev':
717 e.append(-r)
717 e.append(-r)
718 elif k == 'branch':
718 elif k == 'branch':
719 e.append(c.branch())
719 e.append(c.branch())
720 elif k == '-branch':
720 elif k == '-branch':
721 e.append(invert(c.branch()))
721 e.append(invert(c.branch()))
722 elif k == 'desc':
722 elif k == 'desc':
723 e.append(c.description())
723 e.append(c.description())
724 elif k == '-desc':
724 elif k == '-desc':
725 e.append(invert(c.description()))
725 e.append(invert(c.description()))
726 elif k in 'user author':
726 elif k in 'user author':
727 e.append(c.user())
727 e.append(c.user())
728 elif k in '-user -author':
728 elif k in '-user -author':
729 e.append(invert(c.user()))
729 e.append(invert(c.user()))
730 elif k == 'date':
730 elif k == 'date':
731 e.append(c.date()[0])
731 e.append(c.date()[0])
732 elif k == '-date':
732 elif k == '-date':
733 e.append(-c.date()[0])
733 e.append(-c.date()[0])
734 else:
734 else:
735 raise error.ParseError(_("unknown sort key %r") % k)
735 raise error.ParseError(_("unknown sort key %r") % k)
736 e.append(r)
736 e.append(r)
737 l.append(e)
737 l.append(e)
738 l.sort()
738 l.sort()
739 return [e[-1] for e in l]
739 return [e[-1] for e in l]
740
740
741 def tag(repo, subset, x):
741 def tag(repo, subset, x):
742 """``tag(name)``
742 """``tag(name)``
743 The specified tag by name, or all tagged revisions if no name is given.
743 The specified tag by name, or all tagged revisions if no name is given.
744 """
744 """
745 # i18n: "tag" is a keyword
745 # i18n: "tag" is a keyword
746 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
746 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
747 cl = repo.changelog
747 cl = repo.changelog
748 if args:
748 if args:
749 tn = getstring(args[0],
749 tn = getstring(args[0],
750 # i18n: "tag" is a keyword
750 # i18n: "tag" is a keyword
751 _('the argument to tag must be a string'))
751 _('the argument to tag must be a string'))
752 if not repo.tags().get(tn, None):
752 if not repo.tags().get(tn, None):
753 raise util.Abort(_("tag '%s' does not exist") % tn)
753 raise util.Abort(_("tag '%s' does not exist") % tn)
754 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
754 s = set([cl.rev(n) for t, n in repo.tagslist() if t == tn])
755 else:
755 else:
756 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
756 s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
757 return [r for r in subset if r in s]
757 return [r for r in subset if r in s]
758
758
759 def tagged(repo, subset, x):
759 def tagged(repo, subset, x):
760 return tag(repo, subset, x)
760 return tag(repo, subset, x)
761
761
762 def user(repo, subset, x):
762 def user(repo, subset, x):
763 """``user(string)``
763 """``user(string)``
764 User name is string.
764 User name is string.
765 """
765 """
766 return author(repo, subset, x)
766 return author(repo, subset, x)
767
767
768 symbols = {
768 symbols = {
769 "adds": adds,
769 "adds": adds,
770 "all": getall,
770 "all": getall,
771 "ancestor": ancestor,
771 "ancestor": ancestor,
772 "ancestors": ancestors,
772 "ancestors": ancestors,
773 "author": author,
773 "author": author,
774 "bisected": bisected,
774 "bisected": bisected,
775 "bookmark": bookmark,
775 "bookmark": bookmark,
776 "branch": branch,
776 "branch": branch,
777 "children": children,
777 "children": children,
778 "closed": closed,
778 "closed": closed,
779 "contains": contains,
779 "contains": contains,
780 "date": date,
780 "date": date,
781 "descendants": descendants,
781 "descendants": descendants,
782 "file": hasfile,
782 "file": hasfile,
783 "follow": follow,
783 "follow": follow,
784 "grep": grep,
784 "grep": grep,
785 "head": head,
785 "head": head,
786 "heads": heads,
786 "heads": heads,
787 "keyword": keyword,
787 "keyword": keyword,
788 "last": last,
788 "last": last,
789 "limit": limit,
789 "limit": limit,
790 "max": maxrev,
790 "max": maxrev,
791 "min": minrev,
791 "min": minrev,
792 "merge": merge,
792 "merge": merge,
793 "modifies": modifies,
793 "modifies": modifies,
794 "id": node,
794 "id": node,
795 "outgoing": outgoing,
795 "outgoing": outgoing,
796 "p1": p1,
796 "p1": p1,
797 "p2": p2,
797 "p2": p2,
798 "parents": parents,
798 "parents": parents,
799 "present": present,
799 "present": present,
800 "removes": removes,
800 "removes": removes,
801 "reverse": reverse,
801 "reverse": reverse,
802 "rev": rev,
802 "rev": rev,
803 "roots": roots,
803 "roots": roots,
804 "sort": sort,
804 "sort": sort,
805 "tag": tag,
805 "tag": tag,
806 "tagged": tagged,
806 "tagged": tagged,
807 "user": user,
807 "user": user,
808 }
808 }
809
809
810 methods = {
810 methods = {
811 "range": rangeset,
811 "range": rangeset,
812 "string": stringset,
812 "string": stringset,
813 "symbol": symbolset,
813 "symbol": symbolset,
814 "and": andset,
814 "and": andset,
815 "or": orset,
815 "or": orset,
816 "not": notset,
816 "not": notset,
817 "list": listset,
817 "list": listset,
818 "func": func,
818 "func": func,
819 "ancestor": ancestorspec,
819 "ancestor": ancestorspec,
820 "parent": parentspec,
820 "parent": parentspec,
821 "parentpost": p1,
821 "parentpost": p1,
822 }
822 }
823
823
824 def optimize(x, small):
824 def optimize(x, small):
825 if x is None:
825 if x is None:
826 return 0, x
826 return 0, x
827
827
828 smallbonus = 1
828 smallbonus = 1
829 if small:
829 if small:
830 smallbonus = .5
830 smallbonus = .5
831
831
832 op = x[0]
832 op = x[0]
833 if op == 'minus':
833 if op == 'minus':
834 return optimize(('and', x[1], ('not', x[2])), small)
834 return optimize(('and', x[1], ('not', x[2])), small)
835 elif op == 'dagrange':
835 elif op == 'dagrange':
836 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
836 return optimize(('and', ('func', ('symbol', 'descendants'), x[1]),
837 ('func', ('symbol', 'ancestors'), x[2])), small)
837 ('func', ('symbol', 'ancestors'), x[2])), small)
838 elif op == 'dagrangepre':
838 elif op == 'dagrangepre':
839 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
839 return optimize(('func', ('symbol', 'ancestors'), x[1]), small)
840 elif op == 'dagrangepost':
840 elif op == 'dagrangepost':
841 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
841 return optimize(('func', ('symbol', 'descendants'), x[1]), small)
842 elif op == 'rangepre':
842 elif op == 'rangepre':
843 return optimize(('range', ('string', '0'), x[1]), small)
843 return optimize(('range', ('string', '0'), x[1]), small)
844 elif op == 'rangepost':
844 elif op == 'rangepost':
845 return optimize(('range', x[1], ('string', 'tip')), small)
845 return optimize(('range', x[1], ('string', 'tip')), small)
846 elif op == 'negate':
846 elif op == 'negate':
847 return optimize(('string',
847 return optimize(('string',
848 '-' + getstring(x[1], _("can't negate that"))), small)
848 '-' + getstring(x[1], _("can't negate that"))), small)
849 elif op in 'string symbol negate':
849 elif op in 'string symbol negate':
850 return smallbonus, x # single revisions are small
850 return smallbonus, x # single revisions are small
851 elif op == 'and' or op == 'dagrange':
851 elif op == 'and' or op == 'dagrange':
852 wa, ta = optimize(x[1], True)
852 wa, ta = optimize(x[1], True)
853 wb, tb = optimize(x[2], True)
853 wb, tb = optimize(x[2], True)
854 w = min(wa, wb)
854 w = min(wa, wb)
855 if wa > wb:
855 if wa > wb:
856 return w, (op, tb, ta)
856 return w, (op, tb, ta)
857 return w, (op, ta, tb)
857 return w, (op, ta, tb)
858 elif op == 'or':
858 elif op == 'or':
859 wa, ta = optimize(x[1], False)
859 wa, ta = optimize(x[1], False)
860 wb, tb = optimize(x[2], False)
860 wb, tb = optimize(x[2], False)
861 if wb < wa:
861 if wb < wa:
862 wb, wa = wa, wb
862 wb, wa = wa, wb
863 return max(wa, wb), (op, ta, tb)
863 return max(wa, wb), (op, ta, tb)
864 elif op == 'not':
864 elif op == 'not':
865 o = optimize(x[1], not small)
865 o = optimize(x[1], not small)
866 return o[0], (op, o[1])
866 return o[0], (op, o[1])
867 elif op == 'parentpost':
867 elif op == 'parentpost':
868 o = optimize(x[1], small)
868 o = optimize(x[1], small)
869 return o[0], (op, o[1])
869 return o[0], (op, o[1])
870 elif op == 'group':
870 elif op == 'group':
871 return optimize(x[1], small)
871 return optimize(x[1], small)
872 elif op in 'range list parent ancestorspec':
872 elif op in 'range list parent ancestorspec':
873 wa, ta = optimize(x[1], small)
873 wa, ta = optimize(x[1], small)
874 wb, tb = optimize(x[2], small)
874 wb, tb = optimize(x[2], small)
875 return wa + wb, (op, ta, tb)
875 return wa + wb, (op, ta, tb)
876 elif op == 'func':
876 elif op == 'func':
877 f = getstring(x[1], _("not a symbol"))
877 f = getstring(x[1], _("not a symbol"))
878 wa, ta = optimize(x[2], small)
878 wa, ta = optimize(x[2], small)
879 if f in "grep date user author keyword branch file outgoing closed":
879 if f in "grep date user author keyword branch file outgoing closed":
880 w = 10 # slow
880 w = 10 # slow
881 elif f in "modifies adds removes":
881 elif f in "modifies adds removes":
882 w = 30 # slower
882 w = 30 # slower
883 elif f == "contains":
883 elif f == "contains":
884 w = 100 # very slow
884 w = 100 # very slow
885 elif f == "ancestor":
885 elif f == "ancestor":
886 w = 1 * smallbonus
886 w = 1 * smallbonus
887 elif f in "reverse limit":
887 elif f in "reverse limit":
888 w = 0
888 w = 0
889 elif f in "sort":
889 elif f in "sort":
890 w = 10 # assume most sorts look at changelog
890 w = 10 # assume most sorts look at changelog
891 else:
891 else:
892 w = 1
892 w = 1
893 return w + wa, (op, x[1], ta)
893 return w + wa, (op, x[1], ta)
894 return 1, x
894 return 1, x
895
895
896 class revsetalias(object):
896 class revsetalias(object):
897 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
897 funcre = re.compile('^([^(]+)\(([^)]+)\)$')
898 args = ()
898 args = ()
899
899
900 def __init__(self, token, value):
900 def __init__(self, token, value):
901 '''Aliases like:
901 '''Aliases like:
902
902
903 h = heads(default)
903 h = heads(default)
904 b($1) = ancestors($1) - ancestors(default)
904 b($1) = ancestors($1) - ancestors(default)
905 '''
905 '''
906 if isinstance(token, tuple):
906 if isinstance(token, tuple):
907 self.type, self.name = token
907 self.type, self.name = token
908 else:
908 else:
909 m = self.funcre.search(token)
909 m = self.funcre.search(token)
910 if m:
910 if m:
911 self.type = 'func'
911 self.type = 'func'
912 self.name = m.group(1)
912 self.name = m.group(1)
913 self.args = [x.strip() for x in m.group(2).split(',')]
913 self.args = [x.strip() for x in m.group(2).split(',')]
914 else:
914 else:
915 self.type = 'symbol'
915 self.type = 'symbol'
916 self.name = token
916 self.name = token
917
917
918 if isinstance(value, str):
918 if isinstance(value, str):
919 for arg in self.args:
919 for arg in self.args:
920 value = value.replace(arg, repr(arg))
920 value = value.replace(arg, repr(arg))
921 self.replacement, pos = parse(value)
921 self.replacement, pos = parse(value)
922 if pos != len(value):
922 if pos != len(value):
923 raise error.ParseError('invalid token', pos)
923 raise error.ParseError('invalid token', pos)
924 else:
924 else:
925 self.replacement = value
925 self.replacement = value
926
926
927 def match(self, tree):
927 def match(self, tree):
928 if not tree:
928 if not tree:
929 return False
929 return False
930 if tree == (self.type, self.name):
930 if tree == (self.type, self.name):
931 return True
931 return True
932 if tree[0] != self.type:
932 if tree[0] != self.type:
933 return False
933 return False
934 if len(tree) > 1 and tree[1] != ('symbol', self.name):
934 if len(tree) > 1 and tree[1] != ('symbol', self.name):
935 return False
935 return False
936 # 'func' + funcname + args
936 # 'func' + funcname + args
937 if ((self.args and len(tree) != 3) or
937 if ((self.args and len(tree) != 3) or
938 (len(self.args) == 1 and tree[2][0] == 'list') or
938 (len(self.args) == 1 and tree[2][0] == 'list') or
939 (len(self.args) > 1 and (tree[2][0] != 'list' or
939 (len(self.args) > 1 and (tree[2][0] != 'list' or
940 len(tree[2]) - 1 != len(self.args)))):
940 len(tree[2]) - 1 != len(self.args)))):
941 raise error.ParseError('invalid amount of arguments', len(tree) - 2)
941 raise error.ParseError('invalid amount of arguments', len(tree) - 2)
942 return True
942 return True
943
943
944 def replace(self, tree):
944 def replace(self, tree):
945 if tree == (self.type, self.name):
945 if tree == (self.type, self.name):
946 return self.replacement
946 return self.replacement
947 result = self.replacement
947 result = self.replacement
948 def getsubtree(i):
948 def getsubtree(i):
949 if tree[2][0] == 'list':
949 if tree[2][0] == 'list':
950 return tree[2][i + 1]
950 return tree[2][i + 1]
951 return tree[i + 2]
951 return tree[i + 2]
952 for i, v in enumerate(self.args):
952 for i, v in enumerate(self.args):
953 valalias = revsetalias(('string', v), getsubtree(i))
953 valalias = revsetalias(('string', v), getsubtree(i))
954 result = valalias.process(result)
954 result = valalias.process(result)
955 return result
955 return result
956
956
957 def process(self, tree):
957 def process(self, tree):
958 if self.match(tree):
958 if self.match(tree):
959 return self.replace(tree)
959 return self.replace(tree)
960 if isinstance(tree, tuple):
960 if isinstance(tree, tuple):
961 return tuple(map(self.process, tree))
961 return tuple(map(self.process, tree))
962 return tree
962 return tree
963
963
964 def findaliases(ui, tree):
964 def findaliases(ui, tree):
965 for k, v in ui.configitems('revsetalias'):
965 for k, v in ui.configitems('revsetalias'):
966 alias = revsetalias(k, v)
966 alias = revsetalias(k, v)
967 tree = alias.process(tree)
967 tree = alias.process(tree)
968 return tree
968 return tree
969
969
970 parse = parser.parser(tokenize, elements).parse
970 parse = parser.parser(tokenize, elements).parse
971
971
972 def match(ui, spec):
972 def match(ui, spec):
973 if not spec:
973 if not spec:
974 raise error.ParseError(_("empty query"))
974 raise error.ParseError(_("empty query"))
975 tree, pos = parse(spec)
975 tree, pos = parse(spec)
976 if (pos != len(spec)):
976 if (pos != len(spec)):
977 raise error.ParseError("invalid token", pos)
977 raise error.ParseError("invalid token", pos)
978 tree = findaliases(ui, tree)
978 tree = findaliases(ui, tree)
979 weight, tree = optimize(tree, True)
979 weight, tree = optimize(tree, True)
980 def mfunc(repo, subset):
980 def mfunc(repo, subset):
981 return getset(repo, subset, tree)
981 return getset(repo, subset, tree)
982 return mfunc
982 return mfunc
983
983
984 def makedoc(topic, doc):
985 return help.makeitemsdoc(topic, doc, '.. predicatesmarker', symbols)
986
987 # tell hggettext to extract docstrings from these functions:
984 # tell hggettext to extract docstrings from these functions:
988 i18nfunctions = symbols.values()
985 i18nfunctions = symbols.values()
@@ -1,365 +1,362 b''
1 # template-filters.py - common template expansion filters
1 # template-filters.py - common template expansion filters
2 #
2 #
3 # Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import cgi, re, os, time, urllib
8 import cgi, re, os, time, urllib
9 import encoding, node, util, help
9 import encoding, node, util
10
10
11 def addbreaks(text):
11 def addbreaks(text):
12 """:addbreaks: Any text. Add an XHTML "<br />" tag before the end of
12 """:addbreaks: Any text. Add an XHTML "<br />" tag before the end of
13 every line except the last.
13 every line except the last.
14 """
14 """
15 return text.replace('\n', '<br/>\n')
15 return text.replace('\n', '<br/>\n')
16
16
17 agescales = [("year", 3600 * 24 * 365),
17 agescales = [("year", 3600 * 24 * 365),
18 ("month", 3600 * 24 * 30),
18 ("month", 3600 * 24 * 30),
19 ("week", 3600 * 24 * 7),
19 ("week", 3600 * 24 * 7),
20 ("day", 3600 * 24),
20 ("day", 3600 * 24),
21 ("hour", 3600),
21 ("hour", 3600),
22 ("minute", 60),
22 ("minute", 60),
23 ("second", 1)]
23 ("second", 1)]
24
24
25 def age(date):
25 def age(date):
26 """:age: Date. Returns a human-readable date/time difference between the
26 """:age: Date. Returns a human-readable date/time difference between the
27 given date/time and the current date/time.
27 given date/time and the current date/time.
28 """
28 """
29
29
30 def plural(t, c):
30 def plural(t, c):
31 if c == 1:
31 if c == 1:
32 return t
32 return t
33 return t + "s"
33 return t + "s"
34 def fmt(t, c):
34 def fmt(t, c):
35 return "%d %s" % (c, plural(t, c))
35 return "%d %s" % (c, plural(t, c))
36
36
37 now = time.time()
37 now = time.time()
38 then = date[0]
38 then = date[0]
39 future = False
39 future = False
40 if then > now:
40 if then > now:
41 future = True
41 future = True
42 delta = max(1, int(then - now))
42 delta = max(1, int(then - now))
43 if delta > agescales[0][1] * 30:
43 if delta > agescales[0][1] * 30:
44 return 'in the distant future'
44 return 'in the distant future'
45 else:
45 else:
46 delta = max(1, int(now - then))
46 delta = max(1, int(now - then))
47 if delta > agescales[0][1] * 2:
47 if delta > agescales[0][1] * 2:
48 return util.shortdate(date)
48 return util.shortdate(date)
49
49
50 for t, s in agescales:
50 for t, s in agescales:
51 n = delta // s
51 n = delta // s
52 if n >= 2 or s == 1:
52 if n >= 2 or s == 1:
53 if future:
53 if future:
54 return '%s from now' % fmt(t, n)
54 return '%s from now' % fmt(t, n)
55 return '%s ago' % fmt(t, n)
55 return '%s ago' % fmt(t, n)
56
56
57 def basename(path):
57 def basename(path):
58 """:basename: Any text. Treats the text as a path, and returns the last
58 """:basename: Any text. Treats the text as a path, and returns the last
59 component of the path after splitting by the path separator
59 component of the path after splitting by the path separator
60 (ignoring trailing separators). For example, "foo/bar/baz" becomes
60 (ignoring trailing separators). For example, "foo/bar/baz" becomes
61 "baz" and "foo/bar//" becomes "bar".
61 "baz" and "foo/bar//" becomes "bar".
62 """
62 """
63 return os.path.basename(path)
63 return os.path.basename(path)
64
64
65 def datefilter(text):
65 def datefilter(text):
66 """:date: Date. Returns a date in a Unix date format, including the
66 """:date: Date. Returns a date in a Unix date format, including the
67 timezone: "Mon Sep 04 15:13:13 2006 0700".
67 timezone: "Mon Sep 04 15:13:13 2006 0700".
68 """
68 """
69 return util.datestr(text)
69 return util.datestr(text)
70
70
71 def domain(author):
71 def domain(author):
72 """:domain: Any text. Finds the first string that looks like an email
72 """:domain: Any text. Finds the first string that looks like an email
73 address, and extracts just the domain component. Example: ``User
73 address, and extracts just the domain component. Example: ``User
74 <user@example.com>`` becomes ``example.com``.
74 <user@example.com>`` becomes ``example.com``.
75 """
75 """
76 f = author.find('@')
76 f = author.find('@')
77 if f == -1:
77 if f == -1:
78 return ''
78 return ''
79 author = author[f + 1:]
79 author = author[f + 1:]
80 f = author.find('>')
80 f = author.find('>')
81 if f >= 0:
81 if f >= 0:
82 author = author[:f]
82 author = author[:f]
83 return author
83 return author
84
84
85 def email(text):
85 def email(text):
86 """:email: Any text. Extracts the first string that looks like an email
86 """:email: Any text. Extracts the first string that looks like an email
87 address. Example: ``User <user@example.com>`` becomes
87 address. Example: ``User <user@example.com>`` becomes
88 ``user@example.com``.
88 ``user@example.com``.
89 """
89 """
90 return util.email(text)
90 return util.email(text)
91
91
92 def escape(text):
92 def escape(text):
93 """:escape: Any text. Replaces the special XML/XHTML characters "&", "<"
93 """:escape: Any text. Replaces the special XML/XHTML characters "&", "<"
94 and ">" with XML entities.
94 and ">" with XML entities.
95 """
95 """
96 return cgi.escape(text, True)
96 return cgi.escape(text, True)
97
97
98 para_re = None
98 para_re = None
99 space_re = None
99 space_re = None
100
100
101 def fill(text, width):
101 def fill(text, width):
102 '''fill many paragraphs.'''
102 '''fill many paragraphs.'''
103 global para_re, space_re
103 global para_re, space_re
104 if para_re is None:
104 if para_re is None:
105 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
105 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
106 space_re = re.compile(r' +')
106 space_re = re.compile(r' +')
107
107
108 def findparas():
108 def findparas():
109 start = 0
109 start = 0
110 while True:
110 while True:
111 m = para_re.search(text, start)
111 m = para_re.search(text, start)
112 if not m:
112 if not m:
113 uctext = unicode(text[start:], encoding.encoding)
113 uctext = unicode(text[start:], encoding.encoding)
114 w = len(uctext)
114 w = len(uctext)
115 while 0 < w and uctext[w - 1].isspace():
115 while 0 < w and uctext[w - 1].isspace():
116 w -= 1
116 w -= 1
117 yield (uctext[:w].encode(encoding.encoding),
117 yield (uctext[:w].encode(encoding.encoding),
118 uctext[w:].encode(encoding.encoding))
118 uctext[w:].encode(encoding.encoding))
119 break
119 break
120 yield text[start:m.start(0)], m.group(1)
120 yield text[start:m.start(0)], m.group(1)
121 start = m.end(1)
121 start = m.end(1)
122
122
123 return "".join([space_re.sub(' ', util.wrap(para, width=width)) + rest
123 return "".join([space_re.sub(' ', util.wrap(para, width=width)) + rest
124 for para, rest in findparas()])
124 for para, rest in findparas()])
125
125
126 def fill68(text):
126 def fill68(text):
127 """:fill68: Any text. Wraps the text to fit in 68 columns."""
127 """:fill68: Any text. Wraps the text to fit in 68 columns."""
128 return fill(text, 68)
128 return fill(text, 68)
129
129
130 def fill76(text):
130 def fill76(text):
131 """:fill76: Any text. Wraps the text to fit in 76 columns."""
131 """:fill76: Any text. Wraps the text to fit in 76 columns."""
132 return fill(text, 76)
132 return fill(text, 76)
133
133
134 def firstline(text):
134 def firstline(text):
135 """:firstline: Any text. Returns the first line of text."""
135 """:firstline: Any text. Returns the first line of text."""
136 try:
136 try:
137 return text.splitlines(True)[0].rstrip('\r\n')
137 return text.splitlines(True)[0].rstrip('\r\n')
138 except IndexError:
138 except IndexError:
139 return ''
139 return ''
140
140
141 def hexfilter(text):
141 def hexfilter(text):
142 """:hex: Any text. Convert a binary Mercurial node identifier into
142 """:hex: Any text. Convert a binary Mercurial node identifier into
143 its long hexadecimal representation.
143 its long hexadecimal representation.
144 """
144 """
145 return node.hex(text)
145 return node.hex(text)
146
146
147 def hgdate(text):
147 def hgdate(text):
148 """:hgdate: Date. Returns the date as a pair of numbers: "1157407993
148 """:hgdate: Date. Returns the date as a pair of numbers: "1157407993
149 25200" (Unix timestamp, timezone offset).
149 25200" (Unix timestamp, timezone offset).
150 """
150 """
151 return "%d %d" % text
151 return "%d %d" % text
152
152
153 def isodate(text):
153 def isodate(text):
154 """:isodate: Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
154 """:isodate: Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
155 +0200".
155 +0200".
156 """
156 """
157 return util.datestr(text, '%Y-%m-%d %H:%M %1%2')
157 return util.datestr(text, '%Y-%m-%d %H:%M %1%2')
158
158
159 def isodatesec(text):
159 def isodatesec(text):
160 """:isodatesec: Date. Returns the date in ISO 8601 format, including
160 """:isodatesec: Date. Returns the date in ISO 8601 format, including
161 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
161 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
162 filter.
162 filter.
163 """
163 """
164 return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2')
164 return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2')
165
165
166 def indent(text, prefix):
166 def indent(text, prefix):
167 '''indent each non-empty line of text after first with prefix.'''
167 '''indent each non-empty line of text after first with prefix.'''
168 lines = text.splitlines()
168 lines = text.splitlines()
169 num_lines = len(lines)
169 num_lines = len(lines)
170 endswithnewline = text[-1:] == '\n'
170 endswithnewline = text[-1:] == '\n'
171 def indenter():
171 def indenter():
172 for i in xrange(num_lines):
172 for i in xrange(num_lines):
173 l = lines[i]
173 l = lines[i]
174 if i and l.strip():
174 if i and l.strip():
175 yield prefix
175 yield prefix
176 yield l
176 yield l
177 if i < num_lines - 1 or endswithnewline:
177 if i < num_lines - 1 or endswithnewline:
178 yield '\n'
178 yield '\n'
179 return "".join(indenter())
179 return "".join(indenter())
180
180
181 def json(obj):
181 def json(obj):
182 if obj is None or obj is False or obj is True:
182 if obj is None or obj is False or obj is True:
183 return {None: 'null', False: 'false', True: 'true'}[obj]
183 return {None: 'null', False: 'false', True: 'true'}[obj]
184 elif isinstance(obj, int) or isinstance(obj, float):
184 elif isinstance(obj, int) or isinstance(obj, float):
185 return str(obj)
185 return str(obj)
186 elif isinstance(obj, str):
186 elif isinstance(obj, str):
187 u = unicode(obj, encoding.encoding, 'replace')
187 u = unicode(obj, encoding.encoding, 'replace')
188 return '"%s"' % jsonescape(u)
188 return '"%s"' % jsonescape(u)
189 elif isinstance(obj, unicode):
189 elif isinstance(obj, unicode):
190 return '"%s"' % jsonescape(obj)
190 return '"%s"' % jsonescape(obj)
191 elif hasattr(obj, 'keys'):
191 elif hasattr(obj, 'keys'):
192 out = []
192 out = []
193 for k, v in obj.iteritems():
193 for k, v in obj.iteritems():
194 s = '%s: %s' % (json(k), json(v))
194 s = '%s: %s' % (json(k), json(v))
195 out.append(s)
195 out.append(s)
196 return '{' + ', '.join(out) + '}'
196 return '{' + ', '.join(out) + '}'
197 elif hasattr(obj, '__iter__'):
197 elif hasattr(obj, '__iter__'):
198 out = []
198 out = []
199 for i in obj:
199 for i in obj:
200 out.append(json(i))
200 out.append(json(i))
201 return '[' + ', '.join(out) + ']'
201 return '[' + ', '.join(out) + ']'
202 else:
202 else:
203 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
203 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
204
204
205 def _uescape(c):
205 def _uescape(c):
206 if ord(c) < 0x80:
206 if ord(c) < 0x80:
207 return c
207 return c
208 else:
208 else:
209 return '\\u%04x' % ord(c)
209 return '\\u%04x' % ord(c)
210
210
211 _escapes = [
211 _escapes = [
212 ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'),
212 ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'),
213 ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'),
213 ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'),
214 ]
214 ]
215
215
216 def jsonescape(s):
216 def jsonescape(s):
217 for k, v in _escapes:
217 for k, v in _escapes:
218 s = s.replace(k, v)
218 s = s.replace(k, v)
219 return ''.join(_uescape(c) for c in s)
219 return ''.join(_uescape(c) for c in s)
220
220
221 def localdate(text):
221 def localdate(text):
222 """:localdate: Date. Converts a date to local date."""
222 """:localdate: Date. Converts a date to local date."""
223 return (text[0], util.makedate()[1])
223 return (text[0], util.makedate()[1])
224
224
225 def nonempty(str):
225 def nonempty(str):
226 """:nonempty: Any text. Returns '(none)' if the string is empty."""
226 """:nonempty: Any text. Returns '(none)' if the string is empty."""
227 return str or "(none)"
227 return str or "(none)"
228
228
229 def obfuscate(text):
229 def obfuscate(text):
230 """:obfuscate: Any text. Returns the input text rendered as a sequence of
230 """:obfuscate: Any text. Returns the input text rendered as a sequence of
231 XML entities.
231 XML entities.
232 """
232 """
233 text = unicode(text, encoding.encoding, 'replace')
233 text = unicode(text, encoding.encoding, 'replace')
234 return ''.join(['&#%d;' % ord(c) for c in text])
234 return ''.join(['&#%d;' % ord(c) for c in text])
235
235
236 def permissions(flags):
236 def permissions(flags):
237 if "l" in flags:
237 if "l" in flags:
238 return "lrwxrwxrwx"
238 return "lrwxrwxrwx"
239 if "x" in flags:
239 if "x" in flags:
240 return "-rwxr-xr-x"
240 return "-rwxr-xr-x"
241 return "-rw-r--r--"
241 return "-rw-r--r--"
242
242
243 def person(author):
243 def person(author):
244 """:person: Any text. Returns the text before an email address."""
244 """:person: Any text. Returns the text before an email address."""
245 if not '@' in author:
245 if not '@' in author:
246 return author
246 return author
247 f = author.find('<')
247 f = author.find('<')
248 if f != -1:
248 if f != -1:
249 return author[:f].rstrip()
249 return author[:f].rstrip()
250 f = author.find('@')
250 f = author.find('@')
251 return author[:f].replace('.', ' ')
251 return author[:f].replace('.', ' ')
252
252
253 def rfc3339date(text):
253 def rfc3339date(text):
254 """:rfc3339date: Date. Returns a date using the Internet date format
254 """:rfc3339date: Date. Returns a date using the Internet date format
255 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
255 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
256 """
256 """
257 return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
257 return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
258
258
259 def rfc822date(text):
259 def rfc822date(text):
260 """:rfc822date: Date. Returns a date using the same format used in email
260 """:rfc822date: Date. Returns a date using the same format used in email
261 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
261 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
262 """
262 """
263 return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
263 return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
264
264
265 def short(text):
265 def short(text):
266 """:short: Changeset hash. Returns the short form of a changeset hash,
266 """:short: Changeset hash. Returns the short form of a changeset hash,
267 i.e. a 12 hexadecimal digit string.
267 i.e. a 12 hexadecimal digit string.
268 """
268 """
269 return text[:12]
269 return text[:12]
270
270
271 def shortdate(text):
271 def shortdate(text):
272 """:shortdate: Date. Returns a date like "2006-09-18"."""
272 """:shortdate: Date. Returns a date like "2006-09-18"."""
273 return util.shortdate(text)
273 return util.shortdate(text)
274
274
275 def stringescape(text):
275 def stringescape(text):
276 return text.encode('string_escape')
276 return text.encode('string_escape')
277
277
278 def stringify(thing):
278 def stringify(thing):
279 """:stringify: Any type. Turns the value into text by converting values into
279 """:stringify: Any type. Turns the value into text by converting values into
280 text and concatenating them.
280 text and concatenating them.
281 """
281 """
282 if hasattr(thing, '__iter__') and not isinstance(thing, str):
282 if hasattr(thing, '__iter__') and not isinstance(thing, str):
283 return "".join([stringify(t) for t in thing if t is not None])
283 return "".join([stringify(t) for t in thing if t is not None])
284 return str(thing)
284 return str(thing)
285
285
286 def strip(text):
286 def strip(text):
287 """:strip: Any text. Strips all leading and trailing whitespace."""
287 """:strip: Any text. Strips all leading and trailing whitespace."""
288 return text.strip()
288 return text.strip()
289
289
290 def stripdir(text):
290 def stripdir(text):
291 """:stripdir: Treat the text as path and strip a directory level, if
291 """:stripdir: Treat the text as path and strip a directory level, if
292 possible. For example, "foo" and "foo/bar" becomes "foo".
292 possible. For example, "foo" and "foo/bar" becomes "foo".
293 """
293 """
294 dir = os.path.dirname(text)
294 dir = os.path.dirname(text)
295 if dir == "":
295 if dir == "":
296 return os.path.basename(text)
296 return os.path.basename(text)
297 else:
297 else:
298 return dir
298 return dir
299
299
300 def tabindent(text):
300 def tabindent(text):
301 """:tabindent: Any text. Returns the text, with every line except the
301 """:tabindent: Any text. Returns the text, with every line except the
302 first starting with a tab character.
302 first starting with a tab character.
303 """
303 """
304 return indent(text, '\t')
304 return indent(text, '\t')
305
305
306 def urlescape(text):
306 def urlescape(text):
307 """:urlescape: Any text. Escapes all "special" characters. For example,
307 """:urlescape: Any text. Escapes all "special" characters. For example,
308 "foo bar" becomes "foo%20bar".
308 "foo bar" becomes "foo%20bar".
309 """
309 """
310 return urllib.quote(text)
310 return urllib.quote(text)
311
311
312 def userfilter(text):
312 def userfilter(text):
313 """:user: Any text. Returns the user portion of an email address."""
313 """:user: Any text. Returns the user portion of an email address."""
314 return util.shortuser(text)
314 return util.shortuser(text)
315
315
316 def xmlescape(text):
316 def xmlescape(text):
317 text = (text
317 text = (text
318 .replace('&', '&amp;')
318 .replace('&', '&amp;')
319 .replace('<', '&lt;')
319 .replace('<', '&lt;')
320 .replace('>', '&gt;')
320 .replace('>', '&gt;')
321 .replace('"', '&quot;')
321 .replace('"', '&quot;')
322 .replace("'", '&#39;')) # &apos; invalid in HTML
322 .replace("'", '&#39;')) # &apos; invalid in HTML
323 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
323 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
324
324
325 filters = {
325 filters = {
326 "addbreaks": addbreaks,
326 "addbreaks": addbreaks,
327 "age": age,
327 "age": age,
328 "basename": basename,
328 "basename": basename,
329 "date": datefilter,
329 "date": datefilter,
330 "domain": domain,
330 "domain": domain,
331 "email": email,
331 "email": email,
332 "escape": escape,
332 "escape": escape,
333 "fill68": fill68,
333 "fill68": fill68,
334 "fill76": fill76,
334 "fill76": fill76,
335 "firstline": firstline,
335 "firstline": firstline,
336 "hex": hexfilter,
336 "hex": hexfilter,
337 "hgdate": hgdate,
337 "hgdate": hgdate,
338 "isodate": isodate,
338 "isodate": isodate,
339 "isodatesec": isodatesec,
339 "isodatesec": isodatesec,
340 "json": json,
340 "json": json,
341 "jsonescape": jsonescape,
341 "jsonescape": jsonescape,
342 "localdate": localdate,
342 "localdate": localdate,
343 "nonempty": nonempty,
343 "nonempty": nonempty,
344 "obfuscate": obfuscate,
344 "obfuscate": obfuscate,
345 "permissions": permissions,
345 "permissions": permissions,
346 "person": person,
346 "person": person,
347 "rfc3339date": rfc3339date,
347 "rfc3339date": rfc3339date,
348 "rfc822date": rfc822date,
348 "rfc822date": rfc822date,
349 "short": short,
349 "short": short,
350 "shortdate": shortdate,
350 "shortdate": shortdate,
351 "stringescape": stringescape,
351 "stringescape": stringescape,
352 "stringify": stringify,
352 "stringify": stringify,
353 "strip": strip,
353 "strip": strip,
354 "stripdir": stripdir,
354 "stripdir": stripdir,
355 "tabindent": tabindent,
355 "tabindent": tabindent,
356 "urlescape": urlescape,
356 "urlescape": urlescape,
357 "user": userfilter,
357 "user": userfilter,
358 "xmlescape": xmlescape,
358 "xmlescape": xmlescape,
359 }
359 }
360
360
361 def makedoc(topic, doc):
362 return help.makeitemsdoc(topic, doc, '.. filtersmarker', filters)
363
364 # tell hggettext to extract docstrings from these functions:
361 # tell hggettext to extract docstrings from these functions:
365 i18nfunctions = filters.values()
362 i18nfunctions = filters.values()
@@ -1,320 +1,317 b''
1 # templatekw.py - common changeset template keywords
1 # templatekw.py - common changeset template keywords
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex
8 from node import hex
9 import patch, util, error, help
9 import patch, util, error
10
10
11 def showlist(name, values, plural=None, **args):
11 def showlist(name, values, plural=None, **args):
12 '''expand set of values.
12 '''expand set of values.
13 name is name of key in template map.
13 name is name of key in template map.
14 values is list of strings or dicts.
14 values is list of strings or dicts.
15 plural is plural of name, if not simply name + 's'.
15 plural is plural of name, if not simply name + 's'.
16
16
17 expansion works like this, given name 'foo'.
17 expansion works like this, given name 'foo'.
18
18
19 if values is empty, expand 'no_foos'.
19 if values is empty, expand 'no_foos'.
20
20
21 if 'foo' not in template map, return values as a string,
21 if 'foo' not in template map, return values as a string,
22 joined by space.
22 joined by space.
23
23
24 expand 'start_foos'.
24 expand 'start_foos'.
25
25
26 for each value, expand 'foo'. if 'last_foo' in template
26 for each value, expand 'foo'. if 'last_foo' in template
27 map, expand it instead of 'foo' for last key.
27 map, expand it instead of 'foo' for last key.
28
28
29 expand 'end_foos'.
29 expand 'end_foos'.
30 '''
30 '''
31 templ = args['templ']
31 templ = args['templ']
32 if plural:
32 if plural:
33 names = plural
33 names = plural
34 else: names = name + 's'
34 else: names = name + 's'
35 if not values:
35 if not values:
36 noname = 'no_' + names
36 noname = 'no_' + names
37 if noname in templ:
37 if noname in templ:
38 yield templ(noname, **args)
38 yield templ(noname, **args)
39 return
39 return
40 if name not in templ:
40 if name not in templ:
41 if isinstance(values[0], str):
41 if isinstance(values[0], str):
42 yield ' '.join(values)
42 yield ' '.join(values)
43 else:
43 else:
44 for v in values:
44 for v in values:
45 yield dict(v, **args)
45 yield dict(v, **args)
46 return
46 return
47 startname = 'start_' + names
47 startname = 'start_' + names
48 if startname in templ:
48 if startname in templ:
49 yield templ(startname, **args)
49 yield templ(startname, **args)
50 vargs = args.copy()
50 vargs = args.copy()
51 def one(v, tag=name):
51 def one(v, tag=name):
52 try:
52 try:
53 vargs.update(v)
53 vargs.update(v)
54 except (AttributeError, ValueError):
54 except (AttributeError, ValueError):
55 try:
55 try:
56 for a, b in v:
56 for a, b in v:
57 vargs[a] = b
57 vargs[a] = b
58 except ValueError:
58 except ValueError:
59 vargs[name] = v
59 vargs[name] = v
60 return templ(tag, **vargs)
60 return templ(tag, **vargs)
61 lastname = 'last_' + name
61 lastname = 'last_' + name
62 if lastname in templ:
62 if lastname in templ:
63 last = values.pop()
63 last = values.pop()
64 else:
64 else:
65 last = None
65 last = None
66 for v in values:
66 for v in values:
67 yield one(v)
67 yield one(v)
68 if last is not None:
68 if last is not None:
69 yield one(last, tag=lastname)
69 yield one(last, tag=lastname)
70 endname = 'end_' + names
70 endname = 'end_' + names
71 if endname in templ:
71 if endname in templ:
72 yield templ(endname, **args)
72 yield templ(endname, **args)
73
73
74 def getfiles(repo, ctx, revcache):
74 def getfiles(repo, ctx, revcache):
75 if 'files' not in revcache:
75 if 'files' not in revcache:
76 revcache['files'] = repo.status(ctx.p1().node(), ctx.node())[:3]
76 revcache['files'] = repo.status(ctx.p1().node(), ctx.node())[:3]
77 return revcache['files']
77 return revcache['files']
78
78
79 def getlatesttags(repo, ctx, cache):
79 def getlatesttags(repo, ctx, cache):
80 '''return date, distance and name for the latest tag of rev'''
80 '''return date, distance and name for the latest tag of rev'''
81
81
82 if 'latesttags' not in cache:
82 if 'latesttags' not in cache:
83 # Cache mapping from rev to a tuple with tag date, tag
83 # Cache mapping from rev to a tuple with tag date, tag
84 # distance and tag name
84 # distance and tag name
85 cache['latesttags'] = {-1: (0, 0, 'null')}
85 cache['latesttags'] = {-1: (0, 0, 'null')}
86 latesttags = cache['latesttags']
86 latesttags = cache['latesttags']
87
87
88 rev = ctx.rev()
88 rev = ctx.rev()
89 todo = [rev]
89 todo = [rev]
90 while todo:
90 while todo:
91 rev = todo.pop()
91 rev = todo.pop()
92 if rev in latesttags:
92 if rev in latesttags:
93 continue
93 continue
94 ctx = repo[rev]
94 ctx = repo[rev]
95 tags = [t for t in ctx.tags() if repo.tagtype(t) == 'global']
95 tags = [t for t in ctx.tags() if repo.tagtype(t) == 'global']
96 if tags:
96 if tags:
97 latesttags[rev] = ctx.date()[0], 0, ':'.join(sorted(tags))
97 latesttags[rev] = ctx.date()[0], 0, ':'.join(sorted(tags))
98 continue
98 continue
99 try:
99 try:
100 # The tuples are laid out so the right one can be found by
100 # The tuples are laid out so the right one can be found by
101 # comparison.
101 # comparison.
102 pdate, pdist, ptag = max(
102 pdate, pdist, ptag = max(
103 latesttags[p.rev()] for p in ctx.parents())
103 latesttags[p.rev()] for p in ctx.parents())
104 except KeyError:
104 except KeyError:
105 # Cache miss - recurse
105 # Cache miss - recurse
106 todo.append(rev)
106 todo.append(rev)
107 todo.extend(p.rev() for p in ctx.parents())
107 todo.extend(p.rev() for p in ctx.parents())
108 continue
108 continue
109 latesttags[rev] = pdate, pdist + 1, ptag
109 latesttags[rev] = pdate, pdist + 1, ptag
110 return latesttags[rev]
110 return latesttags[rev]
111
111
112 def getrenamedfn(repo, endrev=None):
112 def getrenamedfn(repo, endrev=None):
113 rcache = {}
113 rcache = {}
114 if endrev is None:
114 if endrev is None:
115 endrev = len(repo)
115 endrev = len(repo)
116
116
117 def getrenamed(fn, rev):
117 def getrenamed(fn, rev):
118 '''looks up all renames for a file (up to endrev) the first
118 '''looks up all renames for a file (up to endrev) the first
119 time the file is given. It indexes on the changerev and only
119 time the file is given. It indexes on the changerev and only
120 parses the manifest if linkrev != changerev.
120 parses the manifest if linkrev != changerev.
121 Returns rename info for fn at changerev rev.'''
121 Returns rename info for fn at changerev rev.'''
122 if fn not in rcache:
122 if fn not in rcache:
123 rcache[fn] = {}
123 rcache[fn] = {}
124 fl = repo.file(fn)
124 fl = repo.file(fn)
125 for i in fl:
125 for i in fl:
126 lr = fl.linkrev(i)
126 lr = fl.linkrev(i)
127 renamed = fl.renamed(fl.node(i))
127 renamed = fl.renamed(fl.node(i))
128 rcache[fn][lr] = renamed
128 rcache[fn][lr] = renamed
129 if lr >= endrev:
129 if lr >= endrev:
130 break
130 break
131 if rev in rcache[fn]:
131 if rev in rcache[fn]:
132 return rcache[fn][rev]
132 return rcache[fn][rev]
133
133
134 # If linkrev != rev (i.e. rev not found in rcache) fallback to
134 # If linkrev != rev (i.e. rev not found in rcache) fallback to
135 # filectx logic.
135 # filectx logic.
136 try:
136 try:
137 return repo[rev][fn].renamed()
137 return repo[rev][fn].renamed()
138 except error.LookupError:
138 except error.LookupError:
139 return None
139 return None
140
140
141 return getrenamed
141 return getrenamed
142
142
143
143
144 def showauthor(repo, ctx, templ, **args):
144 def showauthor(repo, ctx, templ, **args):
145 """:author: String. The unmodified author of the changeset."""
145 """:author: String. The unmodified author of the changeset."""
146 return ctx.user()
146 return ctx.user()
147
147
148 def showbranch(**args):
148 def showbranch(**args):
149 """:branch: String. The name of the branch on which the changeset was
149 """:branch: String. The name of the branch on which the changeset was
150 committed.
150 committed.
151 """
151 """
152 return args['ctx'].branch()
152 return args['ctx'].branch()
153
153
154 def showbranches(**args):
154 def showbranches(**args):
155 """:branches: List of strings. The name of the branch on which the
155 """:branches: List of strings. The name of the branch on which the
156 changeset was committed. Will be empty if the branch name was
156 changeset was committed. Will be empty if the branch name was
157 default.
157 default.
158 """
158 """
159 branch = args['ctx'].branch()
159 branch = args['ctx'].branch()
160 if branch != 'default':
160 if branch != 'default':
161 return showlist('branch', [branch], plural='branches', **args)
161 return showlist('branch', [branch], plural='branches', **args)
162
162
163 def showbookmarks(**args):
163 def showbookmarks(**args):
164 """:bookmarks: List of strings. Any bookmarks associated with the
164 """:bookmarks: List of strings. Any bookmarks associated with the
165 changeset.
165 changeset.
166 """
166 """
167 bookmarks = args['ctx'].bookmarks()
167 bookmarks = args['ctx'].bookmarks()
168 return showlist('bookmark', bookmarks, **args)
168 return showlist('bookmark', bookmarks, **args)
169
169
170 def showchildren(**args):
170 def showchildren(**args):
171 """:children: List of strings. The children of the changeset."""
171 """:children: List of strings. The children of the changeset."""
172 ctx = args['ctx']
172 ctx = args['ctx']
173 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
173 childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
174 return showlist('children', childrevs, **args)
174 return showlist('children', childrevs, **args)
175
175
176 def showdate(repo, ctx, templ, **args):
176 def showdate(repo, ctx, templ, **args):
177 """:date: Date information. The date when the changeset was committed."""
177 """:date: Date information. The date when the changeset was committed."""
178 return ctx.date()
178 return ctx.date()
179
179
180 def showdescription(repo, ctx, templ, **args):
180 def showdescription(repo, ctx, templ, **args):
181 """:desc: String. The text of the changeset description."""
181 """:desc: String. The text of the changeset description."""
182 return ctx.description().strip()
182 return ctx.description().strip()
183
183
184 def showdiffstat(repo, ctx, templ, **args):
184 def showdiffstat(repo, ctx, templ, **args):
185 """:diffstat: String. Statistics of changes with the following format:
185 """:diffstat: String. Statistics of changes with the following format:
186 "modified files: +added/-removed lines"
186 "modified files: +added/-removed lines"
187 """
187 """
188 files, adds, removes = 0, 0, 0
188 files, adds, removes = 0, 0, 0
189 for i in patch.diffstatdata(util.iterlines(ctx.diff())):
189 for i in patch.diffstatdata(util.iterlines(ctx.diff())):
190 files += 1
190 files += 1
191 adds += i[1]
191 adds += i[1]
192 removes += i[2]
192 removes += i[2]
193 return '%s: +%s/-%s' % (files, adds, removes)
193 return '%s: +%s/-%s' % (files, adds, removes)
194
194
195 def showextras(**args):
195 def showextras(**args):
196 templ = args['templ']
196 templ = args['templ']
197 for key, value in sorted(args['ctx'].extra().items()):
197 for key, value in sorted(args['ctx'].extra().items()):
198 args = args.copy()
198 args = args.copy()
199 args.update(dict(key=key, value=value))
199 args.update(dict(key=key, value=value))
200 yield templ('extra', **args)
200 yield templ('extra', **args)
201
201
202 def showfileadds(**args):
202 def showfileadds(**args):
203 """:file_adds: List of strings. Files added by this changeset."""
203 """:file_adds: List of strings. Files added by this changeset."""
204 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
204 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
205 return showlist('file_add', getfiles(repo, ctx, revcache)[1], **args)
205 return showlist('file_add', getfiles(repo, ctx, revcache)[1], **args)
206
206
207 def showfilecopies(**args):
207 def showfilecopies(**args):
208 """:file_copies: List of strings. Files copied in this changeset with
208 """:file_copies: List of strings. Files copied in this changeset with
209 their sources.
209 their sources.
210 """
210 """
211 cache, ctx = args['cache'], args['ctx']
211 cache, ctx = args['cache'], args['ctx']
212 copies = args['revcache'].get('copies')
212 copies = args['revcache'].get('copies')
213 if copies is None:
213 if copies is None:
214 if 'getrenamed' not in cache:
214 if 'getrenamed' not in cache:
215 cache['getrenamed'] = getrenamedfn(args['repo'])
215 cache['getrenamed'] = getrenamedfn(args['repo'])
216 copies = []
216 copies = []
217 getrenamed = cache['getrenamed']
217 getrenamed = cache['getrenamed']
218 for fn in ctx.files():
218 for fn in ctx.files():
219 rename = getrenamed(fn, ctx.rev())
219 rename = getrenamed(fn, ctx.rev())
220 if rename:
220 if rename:
221 copies.append((fn, rename[0]))
221 copies.append((fn, rename[0]))
222
222
223 c = [{'name': x[0], 'source': x[1]} for x in copies]
223 c = [{'name': x[0], 'source': x[1]} for x in copies]
224 return showlist('file_copy', c, plural='file_copies', **args)
224 return showlist('file_copy', c, plural='file_copies', **args)
225
225
226 # showfilecopiesswitch() displays file copies only if copy records are
226 # showfilecopiesswitch() displays file copies only if copy records are
227 # provided before calling the templater, usually with a --copies
227 # provided before calling the templater, usually with a --copies
228 # command line switch.
228 # command line switch.
229 def showfilecopiesswitch(**args):
229 def showfilecopiesswitch(**args):
230 """:file_copies_switch: List of strings. Like "file_copies" but displayed
230 """:file_copies_switch: List of strings. Like "file_copies" but displayed
231 only if the --copied switch is set.
231 only if the --copied switch is set.
232 """
232 """
233 copies = args['revcache'].get('copies') or []
233 copies = args['revcache'].get('copies') or []
234 c = [{'name': x[0], 'source': x[1]} for x in copies]
234 c = [{'name': x[0], 'source': x[1]} for x in copies]
235 return showlist('file_copy', c, plural='file_copies', **args)
235 return showlist('file_copy', c, plural='file_copies', **args)
236
236
237 def showfiledels(**args):
237 def showfiledels(**args):
238 """:file_dels: List of strings. Files removed by this changeset."""
238 """:file_dels: List of strings. Files removed by this changeset."""
239 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
239 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
240 return showlist('file_del', getfiles(repo, ctx, revcache)[2], **args)
240 return showlist('file_del', getfiles(repo, ctx, revcache)[2], **args)
241
241
242 def showfilemods(**args):
242 def showfilemods(**args):
243 """:file_mods: List of strings. Files modified by this changeset."""
243 """:file_mods: List of strings. Files modified by this changeset."""
244 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
244 repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
245 return showlist('file_mod', getfiles(repo, ctx, revcache)[0], **args)
245 return showlist('file_mod', getfiles(repo, ctx, revcache)[0], **args)
246
246
247 def showfiles(**args):
247 def showfiles(**args):
248 """:files: List of strings. All files modified, added, or removed by this
248 """:files: List of strings. All files modified, added, or removed by this
249 changeset.
249 changeset.
250 """
250 """
251 return showlist('file', args['ctx'].files(), **args)
251 return showlist('file', args['ctx'].files(), **args)
252
252
253 def showlatesttag(repo, ctx, templ, cache, **args):
253 def showlatesttag(repo, ctx, templ, cache, **args):
254 """:latesttag: String. Most recent global tag in the ancestors of this
254 """:latesttag: String. Most recent global tag in the ancestors of this
255 changeset.
255 changeset.
256 """
256 """
257 return getlatesttags(repo, ctx, cache)[2]
257 return getlatesttags(repo, ctx, cache)[2]
258
258
259 def showlatesttagdistance(repo, ctx, templ, cache, **args):
259 def showlatesttagdistance(repo, ctx, templ, cache, **args):
260 """:latesttagdistance: Integer. Longest path to the latest tag."""
260 """:latesttagdistance: Integer. Longest path to the latest tag."""
261 return getlatesttags(repo, ctx, cache)[1]
261 return getlatesttags(repo, ctx, cache)[1]
262
262
263 def showmanifest(**args):
263 def showmanifest(**args):
264 repo, ctx, templ = args['repo'], args['ctx'], args['templ']
264 repo, ctx, templ = args['repo'], args['ctx'], args['templ']
265 args = args.copy()
265 args = args.copy()
266 args.update(dict(rev=repo.manifest.rev(ctx.changeset()[0]),
266 args.update(dict(rev=repo.manifest.rev(ctx.changeset()[0]),
267 node=hex(ctx.changeset()[0])))
267 node=hex(ctx.changeset()[0])))
268 return templ('manifest', **args)
268 return templ('manifest', **args)
269
269
270 def shownode(repo, ctx, templ, **args):
270 def shownode(repo, ctx, templ, **args):
271 """:node: String. The changeset identification hash, as a 40 hexadecimal
271 """:node: String. The changeset identification hash, as a 40 hexadecimal
272 digit string.
272 digit string.
273 """
273 """
274 return ctx.hex()
274 return ctx.hex()
275
275
276 def showrev(repo, ctx, templ, **args):
276 def showrev(repo, ctx, templ, **args):
277 """:rev: Integer. The repository-local changeset revision number."""
277 """:rev: Integer. The repository-local changeset revision number."""
278 return ctx.rev()
278 return ctx.rev()
279
279
280 def showtags(**args):
280 def showtags(**args):
281 """:tags: List of strings. Any tags associated with the changeset."""
281 """:tags: List of strings. Any tags associated with the changeset."""
282 return showlist('tag', args['ctx'].tags(), **args)
282 return showlist('tag', args['ctx'].tags(), **args)
283
283
284 # keywords are callables like:
284 # keywords are callables like:
285 # fn(repo, ctx, templ, cache, revcache, **args)
285 # fn(repo, ctx, templ, cache, revcache, **args)
286 # with:
286 # with:
287 # repo - current repository instance
287 # repo - current repository instance
288 # ctx - the changectx being displayed
288 # ctx - the changectx being displayed
289 # templ - the templater instance
289 # templ - the templater instance
290 # cache - a cache dictionary for the whole templater run
290 # cache - a cache dictionary for the whole templater run
291 # revcache - a cache dictionary for the current revision
291 # revcache - a cache dictionary for the current revision
292 keywords = {
292 keywords = {
293 'author': showauthor,
293 'author': showauthor,
294 'branch': showbranch,
294 'branch': showbranch,
295 'branches': showbranches,
295 'branches': showbranches,
296 'bookmarks': showbookmarks,
296 'bookmarks': showbookmarks,
297 'children': showchildren,
297 'children': showchildren,
298 'date': showdate,
298 'date': showdate,
299 'desc': showdescription,
299 'desc': showdescription,
300 'diffstat': showdiffstat,
300 'diffstat': showdiffstat,
301 'extras': showextras,
301 'extras': showextras,
302 'file_adds': showfileadds,
302 'file_adds': showfileadds,
303 'file_copies': showfilecopies,
303 'file_copies': showfilecopies,
304 'file_copies_switch': showfilecopiesswitch,
304 'file_copies_switch': showfilecopiesswitch,
305 'file_dels': showfiledels,
305 'file_dels': showfiledels,
306 'file_mods': showfilemods,
306 'file_mods': showfilemods,
307 'files': showfiles,
307 'files': showfiles,
308 'latesttag': showlatesttag,
308 'latesttag': showlatesttag,
309 'latesttagdistance': showlatesttagdistance,
309 'latesttagdistance': showlatesttagdistance,
310 'manifest': showmanifest,
310 'manifest': showmanifest,
311 'node': shownode,
311 'node': shownode,
312 'rev': showrev,
312 'rev': showrev,
313 'tags': showtags,
313 'tags': showtags,
314 }
314 }
315
315
316 def makedoc(topic, doc):
317 return help.makeitemsdoc(topic, doc, '.. keywordsmarker', keywords)
318
319 # tell hggettext to extract docstrings from these functions:
316 # tell hggettext to extract docstrings from these functions:
320 i18nfunctions = keywords.values()
317 i18nfunctions = keywords.values()
General Comments 0
You need to be logged in to leave comments. Login now