##// END OF EJS Templates
merge with stable
Matt Mackall -
r15081:d30ec2d1 merge default
parent child Browse files
Show More
@@ -1,5197 +1,5197 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, difflib, time, tempfile, errno
11 import os, re, difflib, time, tempfile, errno
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, hbisect
14 import archival, changegroup, cmdutil, hbisect
15 import sshserver, hgweb, hgweb.server, commandserver
15 import sshserver, hgweb, hgweb.server, commandserver
16 import merge as mergemod
16 import merge as mergemod
17 import minirst, revset, fileset
17 import minirst, revset, fileset
18 import dagparser, context, simplemerge
18 import dagparser, context, simplemerge
19 import random, setdiscovery, treediscovery, dagutil
19 import random, setdiscovery, treediscovery, dagutil
20
20
21 table = {}
21 table = {}
22
22
23 command = cmdutil.command(table)
23 command = cmdutil.command(table)
24
24
25 # common command options
25 # common command options
26
26
27 globalopts = [
27 globalopts = [
28 ('R', 'repository', '',
28 ('R', 'repository', '',
29 _('repository root directory or name of overlay bundle file'),
29 _('repository root directory or name of overlay bundle file'),
30 _('REPO')),
30 _('REPO')),
31 ('', 'cwd', '',
31 ('', 'cwd', '',
32 _('change working directory'), _('DIR')),
32 _('change working directory'), _('DIR')),
33 ('y', 'noninteractive', None,
33 ('y', 'noninteractive', None,
34 _('do not prompt, automatically pick the first choice for all prompts')),
34 _('do not prompt, automatically pick the first choice for all prompts')),
35 ('q', 'quiet', None, _('suppress output')),
35 ('q', 'quiet', None, _('suppress output')),
36 ('v', 'verbose', None, _('enable additional output')),
36 ('v', 'verbose', None, _('enable additional output')),
37 ('', 'config', [],
37 ('', 'config', [],
38 _('set/override config option (use \'section.name=value\')'),
38 _('set/override config option (use \'section.name=value\')'),
39 _('CONFIG')),
39 _('CONFIG')),
40 ('', 'debug', None, _('enable debugging output')),
40 ('', 'debug', None, _('enable debugging output')),
41 ('', 'debugger', None, _('start debugger')),
41 ('', 'debugger', None, _('start debugger')),
42 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
42 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
43 _('ENCODE')),
43 _('ENCODE')),
44 ('', 'encodingmode', encoding.encodingmode,
44 ('', 'encodingmode', encoding.encodingmode,
45 _('set the charset encoding mode'), _('MODE')),
45 _('set the charset encoding mode'), _('MODE')),
46 ('', 'traceback', None, _('always print a traceback on exception')),
46 ('', 'traceback', None, _('always print a traceback on exception')),
47 ('', 'time', None, _('time how long the command takes')),
47 ('', 'time', None, _('time how long the command takes')),
48 ('', 'profile', None, _('print command execution profile')),
48 ('', 'profile', None, _('print command execution profile')),
49 ('', 'version', None, _('output version information and exit')),
49 ('', 'version', None, _('output version information and exit')),
50 ('h', 'help', None, _('display help and exit')),
50 ('h', 'help', None, _('display help and exit')),
51 ]
51 ]
52
52
53 dryrunopts = [('n', 'dry-run', None,
53 dryrunopts = [('n', 'dry-run', None,
54 _('do not perform actions, just print output'))]
54 _('do not perform actions, just print output'))]
55
55
56 remoteopts = [
56 remoteopts = [
57 ('e', 'ssh', '',
57 ('e', 'ssh', '',
58 _('specify ssh command to use'), _('CMD')),
58 _('specify ssh command to use'), _('CMD')),
59 ('', 'remotecmd', '',
59 ('', 'remotecmd', '',
60 _('specify hg command to run on the remote side'), _('CMD')),
60 _('specify hg command to run on the remote side'), _('CMD')),
61 ('', 'insecure', None,
61 ('', 'insecure', None,
62 _('do not verify server certificate (ignoring web.cacerts config)')),
62 _('do not verify server certificate (ignoring web.cacerts config)')),
63 ]
63 ]
64
64
65 walkopts = [
65 walkopts = [
66 ('I', 'include', [],
66 ('I', 'include', [],
67 _('include names matching the given patterns'), _('PATTERN')),
67 _('include names matching the given patterns'), _('PATTERN')),
68 ('X', 'exclude', [],
68 ('X', 'exclude', [],
69 _('exclude names matching the given patterns'), _('PATTERN')),
69 _('exclude names matching the given patterns'), _('PATTERN')),
70 ]
70 ]
71
71
72 commitopts = [
72 commitopts = [
73 ('m', 'message', '',
73 ('m', 'message', '',
74 _('use text as commit message'), _('TEXT')),
74 _('use text as commit message'), _('TEXT')),
75 ('l', 'logfile', '',
75 ('l', 'logfile', '',
76 _('read commit message from file'), _('FILE')),
76 _('read commit message from file'), _('FILE')),
77 ]
77 ]
78
78
79 commitopts2 = [
79 commitopts2 = [
80 ('d', 'date', '',
80 ('d', 'date', '',
81 _('record the specified date as commit date'), _('DATE')),
81 _('record the specified date as commit date'), _('DATE')),
82 ('u', 'user', '',
82 ('u', 'user', '',
83 _('record the specified user as committer'), _('USER')),
83 _('record the specified user as committer'), _('USER')),
84 ]
84 ]
85
85
86 templateopts = [
86 templateopts = [
87 ('', 'style', '',
87 ('', 'style', '',
88 _('display using template map file'), _('STYLE')),
88 _('display using template map file'), _('STYLE')),
89 ('', 'template', '',
89 ('', 'template', '',
90 _('display with template'), _('TEMPLATE')),
90 _('display with template'), _('TEMPLATE')),
91 ]
91 ]
92
92
93 logopts = [
93 logopts = [
94 ('p', 'patch', None, _('show patch')),
94 ('p', 'patch', None, _('show patch')),
95 ('g', 'git', None, _('use git extended diff format')),
95 ('g', 'git', None, _('use git extended diff format')),
96 ('l', 'limit', '',
96 ('l', 'limit', '',
97 _('limit number of changes displayed'), _('NUM')),
97 _('limit number of changes displayed'), _('NUM')),
98 ('M', 'no-merges', None, _('do not show merges')),
98 ('M', 'no-merges', None, _('do not show merges')),
99 ('', 'stat', None, _('output diffstat-style summary of changes')),
99 ('', 'stat', None, _('output diffstat-style summary of changes')),
100 ] + templateopts
100 ] + templateopts
101
101
102 diffopts = [
102 diffopts = [
103 ('a', 'text', None, _('treat all files as text')),
103 ('a', 'text', None, _('treat all files as text')),
104 ('g', 'git', None, _('use git extended diff format')),
104 ('g', 'git', None, _('use git extended diff format')),
105 ('', 'nodates', None, _('omit dates from diff headers'))
105 ('', 'nodates', None, _('omit dates from diff headers'))
106 ]
106 ]
107
107
108 diffopts2 = [
108 diffopts2 = [
109 ('p', 'show-function', None, _('show which function each change is in')),
109 ('p', 'show-function', None, _('show which function each change is in')),
110 ('', 'reverse', None, _('produce a diff that undoes the changes')),
110 ('', 'reverse', None, _('produce a diff that undoes the changes')),
111 ('w', 'ignore-all-space', None,
111 ('w', 'ignore-all-space', None,
112 _('ignore white space when comparing lines')),
112 _('ignore white space when comparing lines')),
113 ('b', 'ignore-space-change', None,
113 ('b', 'ignore-space-change', None,
114 _('ignore changes in the amount of white space')),
114 _('ignore changes in the amount of white space')),
115 ('B', 'ignore-blank-lines', None,
115 ('B', 'ignore-blank-lines', None,
116 _('ignore changes whose lines are all blank')),
116 _('ignore changes whose lines are all blank')),
117 ('U', 'unified', '',
117 ('U', 'unified', '',
118 _('number of lines of context to show'), _('NUM')),
118 _('number of lines of context to show'), _('NUM')),
119 ('', 'stat', None, _('output diffstat-style summary of changes')),
119 ('', 'stat', None, _('output diffstat-style summary of changes')),
120 ]
120 ]
121
121
122 mergetoolopts = [
122 mergetoolopts = [
123 ('t', 'tool', '', _('specify merge tool')),
123 ('t', 'tool', '', _('specify merge tool')),
124 ]
124 ]
125
125
126 similarityopts = [
126 similarityopts = [
127 ('s', 'similarity', '',
127 ('s', 'similarity', '',
128 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
128 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
129 ]
129 ]
130
130
131 subrepoopts = [
131 subrepoopts = [
132 ('S', 'subrepos', None,
132 ('S', 'subrepos', None,
133 _('recurse into subrepositories'))
133 _('recurse into subrepositories'))
134 ]
134 ]
135
135
136 # Commands start here, listed alphabetically
136 # Commands start here, listed alphabetically
137
137
138 @command('^add',
138 @command('^add',
139 walkopts + subrepoopts + dryrunopts,
139 walkopts + subrepoopts + dryrunopts,
140 _('[OPTION]... [FILE]...'))
140 _('[OPTION]... [FILE]...'))
141 def add(ui, repo, *pats, **opts):
141 def add(ui, repo, *pats, **opts):
142 """add the specified files on the next commit
142 """add the specified files on the next commit
143
143
144 Schedule files to be version controlled and added to the
144 Schedule files to be version controlled and added to the
145 repository.
145 repository.
146
146
147 The files will be added to the repository at the next commit. To
147 The files will be added to the repository at the next commit. To
148 undo an add before that, see :hg:`forget`.
148 undo an add before that, see :hg:`forget`.
149
149
150 If no names are given, add all files to the repository.
150 If no names are given, add all files to the repository.
151
151
152 .. container:: verbose
152 .. container:: verbose
153
153
154 An example showing how new (unknown) files are added
154 An example showing how new (unknown) files are added
155 automatically by :hg:`add`::
155 automatically by :hg:`add`::
156
156
157 $ ls
157 $ ls
158 foo.c
158 foo.c
159 $ hg status
159 $ hg status
160 ? foo.c
160 ? foo.c
161 $ hg add
161 $ hg add
162 adding foo.c
162 adding foo.c
163 $ hg status
163 $ hg status
164 A foo.c
164 A foo.c
165
165
166 Returns 0 if all files are successfully added.
166 Returns 0 if all files are successfully added.
167 """
167 """
168
168
169 m = scmutil.match(repo[None], pats, opts)
169 m = scmutil.match(repo[None], pats, opts)
170 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
170 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
171 opts.get('subrepos'), prefix="")
171 opts.get('subrepos'), prefix="")
172 return rejected and 1 or 0
172 return rejected and 1 or 0
173
173
174 @command('addremove',
174 @command('addremove',
175 similarityopts + walkopts + dryrunopts,
175 similarityopts + walkopts + dryrunopts,
176 _('[OPTION]... [FILE]...'))
176 _('[OPTION]... [FILE]...'))
177 def addremove(ui, repo, *pats, **opts):
177 def addremove(ui, repo, *pats, **opts):
178 """add all new files, delete all missing files
178 """add all new files, delete all missing files
179
179
180 Add all new files and remove all missing files from the
180 Add all new files and remove all missing files from the
181 repository.
181 repository.
182
182
183 New files are ignored if they match any of the patterns in
183 New files are ignored if they match any of the patterns in
184 ``.hgignore``. As with add, these changes take effect at the next
184 ``.hgignore``. As with add, these changes take effect at the next
185 commit.
185 commit.
186
186
187 Use the -s/--similarity option to detect renamed files. With a
187 Use the -s/--similarity option to detect renamed files. With a
188 parameter greater than 0, this compares every removed file with
188 parameter greater than 0, this compares every removed file with
189 every added file and records those similar enough as renames. This
189 every added file and records those similar enough as renames. This
190 option takes a percentage between 0 (disabled) and 100 (files must
190 option takes a percentage between 0 (disabled) and 100 (files must
191 be identical) as its parameter. Detecting renamed files this way
191 be identical) as its parameter. Detecting renamed files this way
192 can be expensive. After using this option, :hg:`status -C` can be
192 can be expensive. After using this option, :hg:`status -C` can be
193 used to check which files were identified as moved or renamed.
193 used to check which files were identified as moved or renamed.
194
194
195 Returns 0 if all files are successfully added.
195 Returns 0 if all files are successfully added.
196 """
196 """
197 try:
197 try:
198 sim = float(opts.get('similarity') or 100)
198 sim = float(opts.get('similarity') or 100)
199 except ValueError:
199 except ValueError:
200 raise util.Abort(_('similarity must be a number'))
200 raise util.Abort(_('similarity must be a number'))
201 if sim < 0 or sim > 100:
201 if sim < 0 or sim > 100:
202 raise util.Abort(_('similarity must be between 0 and 100'))
202 raise util.Abort(_('similarity must be between 0 and 100'))
203 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
203 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
204
204
205 @command('^annotate|blame',
205 @command('^annotate|blame',
206 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
206 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
207 ('', 'follow', None,
207 ('', 'follow', None,
208 _('follow copies/renames and list the filename (DEPRECATED)')),
208 _('follow copies/renames and list the filename (DEPRECATED)')),
209 ('', 'no-follow', None, _("don't follow copies and renames")),
209 ('', 'no-follow', None, _("don't follow copies and renames")),
210 ('a', 'text', None, _('treat all files as text')),
210 ('a', 'text', None, _('treat all files as text')),
211 ('u', 'user', None, _('list the author (long with -v)')),
211 ('u', 'user', None, _('list the author (long with -v)')),
212 ('f', 'file', None, _('list the filename')),
212 ('f', 'file', None, _('list the filename')),
213 ('d', 'date', None, _('list the date (short with -q)')),
213 ('d', 'date', None, _('list the date (short with -q)')),
214 ('n', 'number', None, _('list the revision number (default)')),
214 ('n', 'number', None, _('list the revision number (default)')),
215 ('c', 'changeset', None, _('list the changeset')),
215 ('c', 'changeset', None, _('list the changeset')),
216 ('l', 'line-number', None, _('show line number at the first appearance'))
216 ('l', 'line-number', None, _('show line number at the first appearance'))
217 ] + walkopts,
217 ] + walkopts,
218 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
218 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'))
219 def annotate(ui, repo, *pats, **opts):
219 def annotate(ui, repo, *pats, **opts):
220 """show changeset information by line for each file
220 """show changeset information by line for each file
221
221
222 List changes in files, showing the revision id responsible for
222 List changes in files, showing the revision id responsible for
223 each line
223 each line
224
224
225 This command is useful for discovering when a change was made and
225 This command is useful for discovering when a change was made and
226 by whom.
226 by whom.
227
227
228 Without the -a/--text option, annotate will avoid processing files
228 Without the -a/--text option, annotate will avoid processing files
229 it detects as binary. With -a, annotate will annotate the file
229 it detects as binary. With -a, annotate will annotate the file
230 anyway, although the results will probably be neither useful
230 anyway, although the results will probably be neither useful
231 nor desirable.
231 nor desirable.
232
232
233 Returns 0 on success.
233 Returns 0 on success.
234 """
234 """
235 if opts.get('follow'):
235 if opts.get('follow'):
236 # --follow is deprecated and now just an alias for -f/--file
236 # --follow is deprecated and now just an alias for -f/--file
237 # to mimic the behavior of Mercurial before version 1.5
237 # to mimic the behavior of Mercurial before version 1.5
238 opts['file'] = True
238 opts['file'] = True
239
239
240 datefunc = ui.quiet and util.shortdate or util.datestr
240 datefunc = ui.quiet and util.shortdate or util.datestr
241 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
241 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
242
242
243 if not pats:
243 if not pats:
244 raise util.Abort(_('at least one filename or pattern is required'))
244 raise util.Abort(_('at least one filename or pattern is required'))
245
245
246 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
246 opmap = [('user', ' ', lambda x: ui.shortuser(x[0].user())),
247 ('number', ' ', lambda x: str(x[0].rev())),
247 ('number', ' ', lambda x: str(x[0].rev())),
248 ('changeset', ' ', lambda x: short(x[0].node())),
248 ('changeset', ' ', lambda x: short(x[0].node())),
249 ('date', ' ', getdate),
249 ('date', ' ', getdate),
250 ('file', ' ', lambda x: x[0].path()),
250 ('file', ' ', lambda x: x[0].path()),
251 ('line_number', ':', lambda x: str(x[1])),
251 ('line_number', ':', lambda x: str(x[1])),
252 ]
252 ]
253
253
254 if (not opts.get('user') and not opts.get('changeset')
254 if (not opts.get('user') and not opts.get('changeset')
255 and not opts.get('date') and not opts.get('file')):
255 and not opts.get('date') and not opts.get('file')):
256 opts['number'] = True
256 opts['number'] = True
257
257
258 linenumber = opts.get('line_number') is not None
258 linenumber = opts.get('line_number') is not None
259 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
259 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
260 raise util.Abort(_('at least one of -n/-c is required for -l'))
260 raise util.Abort(_('at least one of -n/-c is required for -l'))
261
261
262 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
262 funcmap = [(func, sep) for op, sep, func in opmap if opts.get(op)]
263 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
263 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
264
264
265 def bad(x, y):
265 def bad(x, y):
266 raise util.Abort("%s: %s" % (x, y))
266 raise util.Abort("%s: %s" % (x, y))
267
267
268 ctx = scmutil.revsingle(repo, opts.get('rev'))
268 ctx = scmutil.revsingle(repo, opts.get('rev'))
269 m = scmutil.match(ctx, pats, opts)
269 m = scmutil.match(ctx, pats, opts)
270 m.bad = bad
270 m.bad = bad
271 follow = not opts.get('no_follow')
271 follow = not opts.get('no_follow')
272 for abs in ctx.walk(m):
272 for abs in ctx.walk(m):
273 fctx = ctx[abs]
273 fctx = ctx[abs]
274 if not opts.get('text') and util.binary(fctx.data()):
274 if not opts.get('text') and util.binary(fctx.data()):
275 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
275 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
276 continue
276 continue
277
277
278 lines = fctx.annotate(follow=follow, linenumber=linenumber)
278 lines = fctx.annotate(follow=follow, linenumber=linenumber)
279 pieces = []
279 pieces = []
280
280
281 for f, sep in funcmap:
281 for f, sep in funcmap:
282 l = [f(n) for n, dummy in lines]
282 l = [f(n) for n, dummy in lines]
283 if l:
283 if l:
284 sized = [(x, encoding.colwidth(x)) for x in l]
284 sized = [(x, encoding.colwidth(x)) for x in l]
285 ml = max([w for x, w in sized])
285 ml = max([w for x, w in sized])
286 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
286 pieces.append(["%s%s%s" % (sep, ' ' * (ml - w), x)
287 for x, w in sized])
287 for x, w in sized])
288
288
289 if pieces:
289 if pieces:
290 for p, l in zip(zip(*pieces), lines):
290 for p, l in zip(zip(*pieces), lines):
291 ui.write("%s: %s" % ("".join(p), l[1]))
291 ui.write("%s: %s" % ("".join(p), l[1]))
292
292
293 @command('archive',
293 @command('archive',
294 [('', 'no-decode', None, _('do not pass files through decoders')),
294 [('', 'no-decode', None, _('do not pass files through decoders')),
295 ('p', 'prefix', '', _('directory prefix for files in archive'),
295 ('p', 'prefix', '', _('directory prefix for files in archive'),
296 _('PREFIX')),
296 _('PREFIX')),
297 ('r', 'rev', '', _('revision to distribute'), _('REV')),
297 ('r', 'rev', '', _('revision to distribute'), _('REV')),
298 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
298 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
299 ] + subrepoopts + walkopts,
299 ] + subrepoopts + walkopts,
300 _('[OPTION]... DEST'))
300 _('[OPTION]... DEST'))
301 def archive(ui, repo, dest, **opts):
301 def archive(ui, repo, dest, **opts):
302 '''create an unversioned archive of a repository revision
302 '''create an unversioned archive of a repository revision
303
303
304 By default, the revision used is the parent of the working
304 By default, the revision used is the parent of the working
305 directory; use -r/--rev to specify a different revision.
305 directory; use -r/--rev to specify a different revision.
306
306
307 The archive type is automatically detected based on file
307 The archive type is automatically detected based on file
308 extension (or override using -t/--type).
308 extension (or override using -t/--type).
309
309
310 Valid types are:
310 Valid types are:
311
311
312 :``files``: a directory full of files (default)
312 :``files``: a directory full of files (default)
313 :``tar``: tar archive, uncompressed
313 :``tar``: tar archive, uncompressed
314 :``tbz2``: tar archive, compressed using bzip2
314 :``tbz2``: tar archive, compressed using bzip2
315 :``tgz``: tar archive, compressed using gzip
315 :``tgz``: tar archive, compressed using gzip
316 :``uzip``: zip archive, uncompressed
316 :``uzip``: zip archive, uncompressed
317 :``zip``: zip archive, compressed using deflate
317 :``zip``: zip archive, compressed using deflate
318
318
319 The exact name of the destination archive or directory is given
319 The exact name of the destination archive or directory is given
320 using a format string; see :hg:`help export` for details.
320 using a format string; see :hg:`help export` for details.
321
321
322 Each member added to an archive file has a directory prefix
322 Each member added to an archive file has a directory prefix
323 prepended. Use -p/--prefix to specify a format string for the
323 prepended. Use -p/--prefix to specify a format string for the
324 prefix. The default is the basename of the archive, with suffixes
324 prefix. The default is the basename of the archive, with suffixes
325 removed.
325 removed.
326
326
327 Returns 0 on success.
327 Returns 0 on success.
328 '''
328 '''
329
329
330 ctx = scmutil.revsingle(repo, opts.get('rev'))
330 ctx = scmutil.revsingle(repo, opts.get('rev'))
331 if not ctx:
331 if not ctx:
332 raise util.Abort(_('no working directory: please specify a revision'))
332 raise util.Abort(_('no working directory: please specify a revision'))
333 node = ctx.node()
333 node = ctx.node()
334 dest = cmdutil.makefilename(repo, dest, node)
334 dest = cmdutil.makefilename(repo, dest, node)
335 if os.path.realpath(dest) == repo.root:
335 if os.path.realpath(dest) == repo.root:
336 raise util.Abort(_('repository root cannot be destination'))
336 raise util.Abort(_('repository root cannot be destination'))
337
337
338 kind = opts.get('type') or archival.guesskind(dest) or 'files'
338 kind = opts.get('type') or archival.guesskind(dest) or 'files'
339 prefix = opts.get('prefix')
339 prefix = opts.get('prefix')
340
340
341 if dest == '-':
341 if dest == '-':
342 if kind == 'files':
342 if kind == 'files':
343 raise util.Abort(_('cannot archive plain files to stdout'))
343 raise util.Abort(_('cannot archive plain files to stdout'))
344 dest = cmdutil.makefileobj(repo, dest)
344 dest = cmdutil.makefileobj(repo, dest)
345 if not prefix:
345 if not prefix:
346 prefix = os.path.basename(repo.root) + '-%h'
346 prefix = os.path.basename(repo.root) + '-%h'
347
347
348 prefix = cmdutil.makefilename(repo, prefix, node)
348 prefix = cmdutil.makefilename(repo, prefix, node)
349 matchfn = scmutil.match(ctx, [], opts)
349 matchfn = scmutil.match(ctx, [], opts)
350 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
350 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
351 matchfn, prefix, subrepos=opts.get('subrepos'))
351 matchfn, prefix, subrepos=opts.get('subrepos'))
352
352
353 @command('backout',
353 @command('backout',
354 [('', 'merge', None, _('merge with old dirstate parent after backout')),
354 [('', 'merge', None, _('merge with old dirstate parent after backout')),
355 ('', 'parent', '', _('parent to choose when backing out merge'), _('REV')),
355 ('', 'parent', '', _('parent to choose when backing out merge'), _('REV')),
356 ('r', 'rev', '', _('revision to backout'), _('REV')),
356 ('r', 'rev', '', _('revision to backout'), _('REV')),
357 ] + mergetoolopts + walkopts + commitopts + commitopts2,
357 ] + mergetoolopts + walkopts + commitopts + commitopts2,
358 _('[OPTION]... [-r] REV'))
358 _('[OPTION]... [-r] REV'))
359 def backout(ui, repo, node=None, rev=None, **opts):
359 def backout(ui, repo, node=None, rev=None, **opts):
360 '''reverse effect of earlier changeset
360 '''reverse effect of earlier changeset
361
361
362 Prepare a new changeset with the effect of REV undone in the
362 Prepare a new changeset with the effect of REV undone in the
363 current working directory.
363 current working directory.
364
364
365 If REV is the parent of the working directory, then this new changeset
365 If REV is the parent of the working directory, then this new changeset
366 is committed automatically. Otherwise, hg needs to merge the
366 is committed automatically. Otherwise, hg needs to merge the
367 changes and the merged result is left uncommitted.
367 changes and the merged result is left uncommitted.
368
368
369 By default, the pending changeset will have one parent,
369 By default, the pending changeset will have one parent,
370 maintaining a linear history. With --merge, the pending changeset
370 maintaining a linear history. With --merge, the pending changeset
371 will instead have two parents: the old parent of the working
371 will instead have two parents: the old parent of the working
372 directory and a new child of REV that simply undoes REV.
372 directory and a new child of REV that simply undoes REV.
373
373
374 Before version 1.7, the behavior without --merge was equivalent to
374 Before version 1.7, the behavior without --merge was equivalent to
375 specifying --merge followed by :hg:`update --clean .` to cancel
375 specifying --merge followed by :hg:`update --clean .` to cancel
376 the merge and leave the child of REV as a head to be merged
376 the merge and leave the child of REV as a head to be merged
377 separately.
377 separately.
378
378
379 See :hg:`help dates` for a list of formats valid for -d/--date.
379 See :hg:`help dates` for a list of formats valid for -d/--date.
380
380
381 Returns 0 on success.
381 Returns 0 on success.
382 '''
382 '''
383 if rev and node:
383 if rev and node:
384 raise util.Abort(_("please specify just one revision"))
384 raise util.Abort(_("please specify just one revision"))
385
385
386 if not rev:
386 if not rev:
387 rev = node
387 rev = node
388
388
389 if not rev:
389 if not rev:
390 raise util.Abort(_("please specify a revision to backout"))
390 raise util.Abort(_("please specify a revision to backout"))
391
391
392 date = opts.get('date')
392 date = opts.get('date')
393 if date:
393 if date:
394 opts['date'] = util.parsedate(date)
394 opts['date'] = util.parsedate(date)
395
395
396 cmdutil.bailifchanged(repo)
396 cmdutil.bailifchanged(repo)
397 node = scmutil.revsingle(repo, rev).node()
397 node = scmutil.revsingle(repo, rev).node()
398
398
399 op1, op2 = repo.dirstate.parents()
399 op1, op2 = repo.dirstate.parents()
400 a = repo.changelog.ancestor(op1, node)
400 a = repo.changelog.ancestor(op1, node)
401 if a != node:
401 if a != node:
402 raise util.Abort(_('cannot backout change on a different branch'))
402 raise util.Abort(_('cannot backout change on a different branch'))
403
403
404 p1, p2 = repo.changelog.parents(node)
404 p1, p2 = repo.changelog.parents(node)
405 if p1 == nullid:
405 if p1 == nullid:
406 raise util.Abort(_('cannot backout a change with no parents'))
406 raise util.Abort(_('cannot backout a change with no parents'))
407 if p2 != nullid:
407 if p2 != nullid:
408 if not opts.get('parent'):
408 if not opts.get('parent'):
409 raise util.Abort(_('cannot backout a merge changeset without '
409 raise util.Abort(_('cannot backout a merge changeset without '
410 '--parent'))
410 '--parent'))
411 p = repo.lookup(opts['parent'])
411 p = repo.lookup(opts['parent'])
412 if p not in (p1, p2):
412 if p not in (p1, p2):
413 raise util.Abort(_('%s is not a parent of %s') %
413 raise util.Abort(_('%s is not a parent of %s') %
414 (short(p), short(node)))
414 (short(p), short(node)))
415 parent = p
415 parent = p
416 else:
416 else:
417 if opts.get('parent'):
417 if opts.get('parent'):
418 raise util.Abort(_('cannot use --parent on non-merge changeset'))
418 raise util.Abort(_('cannot use --parent on non-merge changeset'))
419 parent = p1
419 parent = p1
420
420
421 # the backout should appear on the same branch
421 # the backout should appear on the same branch
422 branch = repo.dirstate.branch()
422 branch = repo.dirstate.branch()
423 hg.clean(repo, node, show_stats=False)
423 hg.clean(repo, node, show_stats=False)
424 repo.dirstate.setbranch(branch)
424 repo.dirstate.setbranch(branch)
425 revert_opts = opts.copy()
425 revert_opts = opts.copy()
426 revert_opts['date'] = None
426 revert_opts['date'] = None
427 revert_opts['all'] = True
427 revert_opts['all'] = True
428 revert_opts['rev'] = hex(parent)
428 revert_opts['rev'] = hex(parent)
429 revert_opts['no_backup'] = None
429 revert_opts['no_backup'] = None
430 revert(ui, repo, **revert_opts)
430 revert(ui, repo, **revert_opts)
431 if not opts.get('merge') and op1 != node:
431 if not opts.get('merge') and op1 != node:
432 try:
432 try:
433 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
433 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
434 return hg.update(repo, op1)
434 return hg.update(repo, op1)
435 finally:
435 finally:
436 ui.setconfig('ui', 'forcemerge', '')
436 ui.setconfig('ui', 'forcemerge', '')
437
437
438 commit_opts = opts.copy()
438 commit_opts = opts.copy()
439 commit_opts['addremove'] = False
439 commit_opts['addremove'] = False
440 if not commit_opts['message'] and not commit_opts['logfile']:
440 if not commit_opts['message'] and not commit_opts['logfile']:
441 # we don't translate commit messages
441 # we don't translate commit messages
442 commit_opts['message'] = "Backed out changeset %s" % short(node)
442 commit_opts['message'] = "Backed out changeset %s" % short(node)
443 commit_opts['force_editor'] = True
443 commit_opts['force_editor'] = True
444 commit(ui, repo, **commit_opts)
444 commit(ui, repo, **commit_opts)
445 def nice(node):
445 def nice(node):
446 return '%d:%s' % (repo.changelog.rev(node), short(node))
446 return '%d:%s' % (repo.changelog.rev(node), short(node))
447 ui.status(_('changeset %s backs out changeset %s\n') %
447 ui.status(_('changeset %s backs out changeset %s\n') %
448 (nice(repo.changelog.tip()), nice(node)))
448 (nice(repo.changelog.tip()), nice(node)))
449 if opts.get('merge') and op1 != node:
449 if opts.get('merge') and op1 != node:
450 hg.clean(repo, op1, show_stats=False)
450 hg.clean(repo, op1, show_stats=False)
451 ui.status(_('merging with changeset %s\n')
451 ui.status(_('merging with changeset %s\n')
452 % nice(repo.changelog.tip()))
452 % nice(repo.changelog.tip()))
453 try:
453 try:
454 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
454 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
455 return hg.merge(repo, hex(repo.changelog.tip()))
455 return hg.merge(repo, hex(repo.changelog.tip()))
456 finally:
456 finally:
457 ui.setconfig('ui', 'forcemerge', '')
457 ui.setconfig('ui', 'forcemerge', '')
458 return 0
458 return 0
459
459
460 @command('bisect',
460 @command('bisect',
461 [('r', 'reset', False, _('reset bisect state')),
461 [('r', 'reset', False, _('reset bisect state')),
462 ('g', 'good', False, _('mark changeset good')),
462 ('g', 'good', False, _('mark changeset good')),
463 ('b', 'bad', False, _('mark changeset bad')),
463 ('b', 'bad', False, _('mark changeset bad')),
464 ('s', 'skip', False, _('skip testing changeset')),
464 ('s', 'skip', False, _('skip testing changeset')),
465 ('e', 'extend', False, _('extend the bisect range')),
465 ('e', 'extend', False, _('extend the bisect range')),
466 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
466 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
467 ('U', 'noupdate', False, _('do not update to target'))],
467 ('U', 'noupdate', False, _('do not update to target'))],
468 _("[-gbsr] [-U] [-c CMD] [REV]"))
468 _("[-gbsr] [-U] [-c CMD] [REV]"))
469 def bisect(ui, repo, rev=None, extra=None, command=None,
469 def bisect(ui, repo, rev=None, extra=None, command=None,
470 reset=None, good=None, bad=None, skip=None, extend=None,
470 reset=None, good=None, bad=None, skip=None, extend=None,
471 noupdate=None):
471 noupdate=None):
472 """subdivision search of changesets
472 """subdivision search of changesets
473
473
474 This command helps to find changesets which introduce problems. To
474 This command helps to find changesets which introduce problems. To
475 use, mark the earliest changeset you know exhibits the problem as
475 use, mark the earliest changeset you know exhibits the problem as
476 bad, then mark the latest changeset which is free from the problem
476 bad, then mark the latest changeset which is free from the problem
477 as good. Bisect will update your working directory to a revision
477 as good. Bisect will update your working directory to a revision
478 for testing (unless the -U/--noupdate option is specified). Once
478 for testing (unless the -U/--noupdate option is specified). Once
479 you have performed tests, mark the working directory as good or
479 you have performed tests, mark the working directory as good or
480 bad, and bisect will either update to another candidate changeset
480 bad, and bisect will either update to another candidate changeset
481 or announce that it has found the bad revision.
481 or announce that it has found the bad revision.
482
482
483 As a shortcut, you can also use the revision argument to mark a
483 As a shortcut, you can also use the revision argument to mark a
484 revision as good or bad without checking it out first.
484 revision as good or bad without checking it out first.
485
485
486 If you supply a command, it will be used for automatic bisection.
486 If you supply a command, it will be used for automatic bisection.
487 Its exit status will be used to mark revisions as good or bad:
487 Its exit status will be used to mark revisions as good or bad:
488 status 0 means good, 125 means to skip the revision, 127
488 status 0 means good, 125 means to skip the revision, 127
489 (command not found) will abort the bisection, and any other
489 (command not found) will abort the bisection, and any other
490 non-zero exit status means the revision is bad.
490 non-zero exit status means the revision is bad.
491
491
492 Returns 0 on success.
492 Returns 0 on success.
493 """
493 """
494 def extendbisectrange(nodes, good):
494 def extendbisectrange(nodes, good):
495 # bisect is incomplete when it ends on a merge node and
495 # bisect is incomplete when it ends on a merge node and
496 # one of the parent was not checked.
496 # one of the parent was not checked.
497 parents = repo[nodes[0]].parents()
497 parents = repo[nodes[0]].parents()
498 if len(parents) > 1:
498 if len(parents) > 1:
499 side = good and state['bad'] or state['good']
499 side = good and state['bad'] or state['good']
500 num = len(set(i.node() for i in parents) & set(side))
500 num = len(set(i.node() for i in parents) & set(side))
501 if num == 1:
501 if num == 1:
502 return parents[0].ancestor(parents[1])
502 return parents[0].ancestor(parents[1])
503 return None
503 return None
504
504
505 def print_result(nodes, good):
505 def print_result(nodes, good):
506 displayer = cmdutil.show_changeset(ui, repo, {})
506 displayer = cmdutil.show_changeset(ui, repo, {})
507 if len(nodes) == 1:
507 if len(nodes) == 1:
508 # narrowed it down to a single revision
508 # narrowed it down to a single revision
509 if good:
509 if good:
510 ui.write(_("The first good revision is:\n"))
510 ui.write(_("The first good revision is:\n"))
511 else:
511 else:
512 ui.write(_("The first bad revision is:\n"))
512 ui.write(_("The first bad revision is:\n"))
513 displayer.show(repo[nodes[0]])
513 displayer.show(repo[nodes[0]])
514 extendnode = extendbisectrange(nodes, good)
514 extendnode = extendbisectrange(nodes, good)
515 if extendnode is not None:
515 if extendnode is not None:
516 ui.write(_('Not all ancestors of this changeset have been'
516 ui.write(_('Not all ancestors of this changeset have been'
517 ' checked.\nUse bisect --extend to continue the '
517 ' checked.\nUse bisect --extend to continue the '
518 'bisection from\nthe common ancestor, %s.\n')
518 'bisection from\nthe common ancestor, %s.\n')
519 % extendnode)
519 % extendnode)
520 else:
520 else:
521 # multiple possible revisions
521 # multiple possible revisions
522 if good:
522 if good:
523 ui.write(_("Due to skipped revisions, the first "
523 ui.write(_("Due to skipped revisions, the first "
524 "good revision could be any of:\n"))
524 "good revision could be any of:\n"))
525 else:
525 else:
526 ui.write(_("Due to skipped revisions, the first "
526 ui.write(_("Due to skipped revisions, the first "
527 "bad revision could be any of:\n"))
527 "bad revision could be any of:\n"))
528 for n in nodes:
528 for n in nodes:
529 displayer.show(repo[n])
529 displayer.show(repo[n])
530 displayer.close()
530 displayer.close()
531
531
532 def check_state(state, interactive=True):
532 def check_state(state, interactive=True):
533 if not state['good'] or not state['bad']:
533 if not state['good'] or not state['bad']:
534 if (good or bad or skip or reset) and interactive:
534 if (good or bad or skip or reset) and interactive:
535 return
535 return
536 if not state['good']:
536 if not state['good']:
537 raise util.Abort(_('cannot bisect (no known good revisions)'))
537 raise util.Abort(_('cannot bisect (no known good revisions)'))
538 else:
538 else:
539 raise util.Abort(_('cannot bisect (no known bad revisions)'))
539 raise util.Abort(_('cannot bisect (no known bad revisions)'))
540 return True
540 return True
541
541
542 # backward compatibility
542 # backward compatibility
543 if rev in "good bad reset init".split():
543 if rev in "good bad reset init".split():
544 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
544 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
545 cmd, rev, extra = rev, extra, None
545 cmd, rev, extra = rev, extra, None
546 if cmd == "good":
546 if cmd == "good":
547 good = True
547 good = True
548 elif cmd == "bad":
548 elif cmd == "bad":
549 bad = True
549 bad = True
550 else:
550 else:
551 reset = True
551 reset = True
552 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
552 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
553 raise util.Abort(_('incompatible arguments'))
553 raise util.Abort(_('incompatible arguments'))
554
554
555 if reset:
555 if reset:
556 p = repo.join("bisect.state")
556 p = repo.join("bisect.state")
557 if os.path.exists(p):
557 if os.path.exists(p):
558 os.unlink(p)
558 os.unlink(p)
559 return
559 return
560
560
561 state = hbisect.load_state(repo)
561 state = hbisect.load_state(repo)
562
562
563 if command:
563 if command:
564 changesets = 1
564 changesets = 1
565 try:
565 try:
566 while changesets:
566 while changesets:
567 # update state
567 # update state
568 status = util.system(command, out=ui.fout)
568 status = util.system(command, out=ui.fout)
569 if status == 125:
569 if status == 125:
570 transition = "skip"
570 transition = "skip"
571 elif status == 0:
571 elif status == 0:
572 transition = "good"
572 transition = "good"
573 # status < 0 means process was killed
573 # status < 0 means process was killed
574 elif status == 127:
574 elif status == 127:
575 raise util.Abort(_("failed to execute %s") % command)
575 raise util.Abort(_("failed to execute %s") % command)
576 elif status < 0:
576 elif status < 0:
577 raise util.Abort(_("%s killed") % command)
577 raise util.Abort(_("%s killed") % command)
578 else:
578 else:
579 transition = "bad"
579 transition = "bad"
580 ctx = scmutil.revsingle(repo, rev)
580 ctx = scmutil.revsingle(repo, rev)
581 rev = None # clear for future iterations
581 rev = None # clear for future iterations
582 state[transition].append(ctx.node())
582 state[transition].append(ctx.node())
583 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
583 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
584 check_state(state, interactive=False)
584 check_state(state, interactive=False)
585 # bisect
585 # bisect
586 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
586 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
587 # update to next check
587 # update to next check
588 cmdutil.bailifchanged(repo)
588 cmdutil.bailifchanged(repo)
589 hg.clean(repo, nodes[0], show_stats=False)
589 hg.clean(repo, nodes[0], show_stats=False)
590 finally:
590 finally:
591 hbisect.save_state(repo, state)
591 hbisect.save_state(repo, state)
592 print_result(nodes, good)
592 print_result(nodes, good)
593 return
593 return
594
594
595 # update state
595 # update state
596
596
597 if rev:
597 if rev:
598 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
598 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
599 else:
599 else:
600 nodes = [repo.lookup('.')]
600 nodes = [repo.lookup('.')]
601
601
602 if good or bad or skip:
602 if good or bad or skip:
603 if good:
603 if good:
604 state['good'] += nodes
604 state['good'] += nodes
605 elif bad:
605 elif bad:
606 state['bad'] += nodes
606 state['bad'] += nodes
607 elif skip:
607 elif skip:
608 state['skip'] += nodes
608 state['skip'] += nodes
609 hbisect.save_state(repo, state)
609 hbisect.save_state(repo, state)
610
610
611 if not check_state(state):
611 if not check_state(state):
612 return
612 return
613
613
614 # actually bisect
614 # actually bisect
615 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
615 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
616 if extend:
616 if extend:
617 if not changesets:
617 if not changesets:
618 extendnode = extendbisectrange(nodes, good)
618 extendnode = extendbisectrange(nodes, good)
619 if extendnode is not None:
619 if extendnode is not None:
620 ui.write(_("Extending search to changeset %d:%s\n"
620 ui.write(_("Extending search to changeset %d:%s\n"
621 % (extendnode.rev(), extendnode)))
621 % (extendnode.rev(), extendnode)))
622 if noupdate:
622 if noupdate:
623 return
623 return
624 cmdutil.bailifchanged(repo)
624 cmdutil.bailifchanged(repo)
625 return hg.clean(repo, extendnode.node())
625 return hg.clean(repo, extendnode.node())
626 raise util.Abort(_("nothing to extend"))
626 raise util.Abort(_("nothing to extend"))
627
627
628 if changesets == 0:
628 if changesets == 0:
629 print_result(nodes, good)
629 print_result(nodes, good)
630 else:
630 else:
631 assert len(nodes) == 1 # only a single node can be tested next
631 assert len(nodes) == 1 # only a single node can be tested next
632 node = nodes[0]
632 node = nodes[0]
633 # compute the approximate number of remaining tests
633 # compute the approximate number of remaining tests
634 tests, size = 0, 2
634 tests, size = 0, 2
635 while size <= changesets:
635 while size <= changesets:
636 tests, size = tests + 1, size * 2
636 tests, size = tests + 1, size * 2
637 rev = repo.changelog.rev(node)
637 rev = repo.changelog.rev(node)
638 ui.write(_("Testing changeset %d:%s "
638 ui.write(_("Testing changeset %d:%s "
639 "(%d changesets remaining, ~%d tests)\n")
639 "(%d changesets remaining, ~%d tests)\n")
640 % (rev, short(node), changesets, tests))
640 % (rev, short(node), changesets, tests))
641 if not noupdate:
641 if not noupdate:
642 cmdutil.bailifchanged(repo)
642 cmdutil.bailifchanged(repo)
643 return hg.clean(repo, node)
643 return hg.clean(repo, node)
644
644
645 @command('bookmarks',
645 @command('bookmarks',
646 [('f', 'force', False, _('force')),
646 [('f', 'force', False, _('force')),
647 ('r', 'rev', '', _('revision'), _('REV')),
647 ('r', 'rev', '', _('revision'), _('REV')),
648 ('d', 'delete', False, _('delete a given bookmark')),
648 ('d', 'delete', False, _('delete a given bookmark')),
649 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
649 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
650 ('i', 'inactive', False, _('do not mark a new bookmark active'))],
650 ('i', 'inactive', False, _('do not mark a new bookmark active'))],
651 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
651 _('hg bookmarks [-f] [-d] [-i] [-m NAME] [-r REV] [NAME]'))
652 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
652 def bookmark(ui, repo, mark=None, rev=None, force=False, delete=False,
653 rename=None, inactive=False):
653 rename=None, inactive=False):
654 '''track a line of development with movable markers
654 '''track a line of development with movable markers
655
655
656 Bookmarks are pointers to certain commits that move when
656 Bookmarks are pointers to certain commits that move when
657 committing. Bookmarks are local. They can be renamed, copied and
657 committing. Bookmarks are local. They can be renamed, copied and
658 deleted. It is possible to use bookmark names in :hg:`merge` and
658 deleted. It is possible to use bookmark names in :hg:`merge` and
659 :hg:`update` to merge and update respectively to a given bookmark.
659 :hg:`update` to merge and update respectively to a given bookmark.
660
660
661 You can use :hg:`bookmark NAME` to set a bookmark on the working
661 You can use :hg:`bookmark NAME` to set a bookmark on the working
662 directory's parent revision with the given name. If you specify
662 directory's parent revision with the given name. If you specify
663 a revision using -r REV (where REV may be an existing bookmark),
663 a revision using -r REV (where REV may be an existing bookmark),
664 the bookmark is assigned to that revision.
664 the bookmark is assigned to that revision.
665
665
666 Bookmarks can be pushed and pulled between repositories (see :hg:`help
666 Bookmarks can be pushed and pulled between repositories (see :hg:`help
667 push` and :hg:`help pull`). This requires both the local and remote
667 push` and :hg:`help pull`). This requires both the local and remote
668 repositories to support bookmarks. For versions prior to 1.8, this means
668 repositories to support bookmarks. For versions prior to 1.8, this means
669 the bookmarks extension must be enabled.
669 the bookmarks extension must be enabled.
670 '''
670 '''
671 hexfn = ui.debugflag and hex or short
671 hexfn = ui.debugflag and hex or short
672 marks = repo._bookmarks
672 marks = repo._bookmarks
673 cur = repo.changectx('.').node()
673 cur = repo.changectx('.').node()
674
674
675 if rename:
675 if rename:
676 if rename not in marks:
676 if rename not in marks:
677 raise util.Abort(_("bookmark '%s' does not exist") % rename)
677 raise util.Abort(_("bookmark '%s' does not exist") % rename)
678 if mark in marks and not force:
678 if mark in marks and not force:
679 raise util.Abort(_("bookmark '%s' already exists "
679 raise util.Abort(_("bookmark '%s' already exists "
680 "(use -f to force)") % mark)
680 "(use -f to force)") % mark)
681 if mark is None:
681 if mark is None:
682 raise util.Abort(_("new bookmark name required"))
682 raise util.Abort(_("new bookmark name required"))
683 marks[mark] = marks[rename]
683 marks[mark] = marks[rename]
684 if repo._bookmarkcurrent == rename and not inactive:
684 if repo._bookmarkcurrent == rename and not inactive:
685 bookmarks.setcurrent(repo, mark)
685 bookmarks.setcurrent(repo, mark)
686 del marks[rename]
686 del marks[rename]
687 bookmarks.write(repo)
687 bookmarks.write(repo)
688 return
688 return
689
689
690 if delete:
690 if delete:
691 if mark is None:
691 if mark is None:
692 raise util.Abort(_("bookmark name required"))
692 raise util.Abort(_("bookmark name required"))
693 if mark not in marks:
693 if mark not in marks:
694 raise util.Abort(_("bookmark '%s' does not exist") % mark)
694 raise util.Abort(_("bookmark '%s' does not exist") % mark)
695 if mark == repo._bookmarkcurrent:
695 if mark == repo._bookmarkcurrent:
696 bookmarks.setcurrent(repo, None)
696 bookmarks.setcurrent(repo, None)
697 del marks[mark]
697 del marks[mark]
698 bookmarks.write(repo)
698 bookmarks.write(repo)
699 return
699 return
700
700
701 if mark is not None:
701 if mark is not None:
702 if "\n" in mark:
702 if "\n" in mark:
703 raise util.Abort(_("bookmark name cannot contain newlines"))
703 raise util.Abort(_("bookmark name cannot contain newlines"))
704 mark = mark.strip()
704 mark = mark.strip()
705 if not mark:
705 if not mark:
706 raise util.Abort(_("bookmark names cannot consist entirely of "
706 raise util.Abort(_("bookmark names cannot consist entirely of "
707 "whitespace"))
707 "whitespace"))
708 if inactive and mark == repo._bookmarkcurrent:
708 if inactive and mark == repo._bookmarkcurrent:
709 bookmarks.setcurrent(repo, None)
709 bookmarks.setcurrent(repo, None)
710 return
710 return
711 if mark in marks and not force:
711 if mark in marks and not force:
712 raise util.Abort(_("bookmark '%s' already exists "
712 raise util.Abort(_("bookmark '%s' already exists "
713 "(use -f to force)") % mark)
713 "(use -f to force)") % mark)
714 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
714 if ((mark in repo.branchtags() or mark == repo.dirstate.branch())
715 and not force):
715 and not force):
716 raise util.Abort(
716 raise util.Abort(
717 _("a bookmark cannot have the name of an existing branch"))
717 _("a bookmark cannot have the name of an existing branch"))
718 if rev:
718 if rev:
719 marks[mark] = repo.lookup(rev)
719 marks[mark] = repo.lookup(rev)
720 else:
720 else:
721 marks[mark] = repo.changectx('.').node()
721 marks[mark] = repo.changectx('.').node()
722 if not inactive and repo.changectx('.').node() == marks[mark]:
722 if not inactive and repo.changectx('.').node() == marks[mark]:
723 bookmarks.setcurrent(repo, mark)
723 bookmarks.setcurrent(repo, mark)
724 bookmarks.write(repo)
724 bookmarks.write(repo)
725 return
725 return
726
726
727 if mark is None:
727 if mark is None:
728 if rev:
728 if rev:
729 raise util.Abort(_("bookmark name required"))
729 raise util.Abort(_("bookmark name required"))
730 if len(marks) == 0:
730 if len(marks) == 0:
731 ui.status(_("no bookmarks set\n"))
731 ui.status(_("no bookmarks set\n"))
732 else:
732 else:
733 for bmark, n in sorted(marks.iteritems()):
733 for bmark, n in sorted(marks.iteritems()):
734 current = repo._bookmarkcurrent
734 current = repo._bookmarkcurrent
735 if bmark == current and n == cur:
735 if bmark == current and n == cur:
736 prefix, label = '*', 'bookmarks.current'
736 prefix, label = '*', 'bookmarks.current'
737 else:
737 else:
738 prefix, label = ' ', ''
738 prefix, label = ' ', ''
739
739
740 if ui.quiet:
740 if ui.quiet:
741 ui.write("%s\n" % bmark, label=label)
741 ui.write("%s\n" % bmark, label=label)
742 else:
742 else:
743 ui.write(" %s %-25s %d:%s\n" % (
743 ui.write(" %s %-25s %d:%s\n" % (
744 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
744 prefix, bmark, repo.changelog.rev(n), hexfn(n)),
745 label=label)
745 label=label)
746 return
746 return
747
747
748 @command('branch',
748 @command('branch',
749 [('f', 'force', None,
749 [('f', 'force', None,
750 _('set branch name even if it shadows an existing branch')),
750 _('set branch name even if it shadows an existing branch')),
751 ('C', 'clean', None, _('reset branch name to parent branch name'))],
751 ('C', 'clean', None, _('reset branch name to parent branch name'))],
752 _('[-fC] [NAME]'))
752 _('[-fC] [NAME]'))
753 def branch(ui, repo, label=None, **opts):
753 def branch(ui, repo, label=None, **opts):
754 """set or show the current branch name
754 """set or show the current branch name
755
755
756 With no argument, show the current branch name. With one argument,
756 With no argument, show the current branch name. With one argument,
757 set the working directory branch name (the branch will not exist
757 set the working directory branch name (the branch will not exist
758 in the repository until the next commit). Standard practice
758 in the repository until the next commit). Standard practice
759 recommends that primary development take place on the 'default'
759 recommends that primary development take place on the 'default'
760 branch.
760 branch.
761
761
762 Unless -f/--force is specified, branch will not let you set a
762 Unless -f/--force is specified, branch will not let you set a
763 branch name that already exists, even if it's inactive.
763 branch name that already exists, even if it's inactive.
764
764
765 Use -C/--clean to reset the working directory branch to that of
765 Use -C/--clean to reset the working directory branch to that of
766 the parent of the working directory, negating a previous branch
766 the parent of the working directory, negating a previous branch
767 change.
767 change.
768
768
769 Use the command :hg:`update` to switch to an existing branch. Use
769 Use the command :hg:`update` to switch to an existing branch. Use
770 :hg:`commit --close-branch` to mark this branch as closed.
770 :hg:`commit --close-branch` to mark this branch as closed.
771
771
772 .. note::
772 .. note::
773
773
774 Branch names are permanent. Use :hg:`bookmark` to create a
774 Branch names are permanent. Use :hg:`bookmark` to create a
775 light-weight bookmark instead. See :hg:`help glossary` for more
775 light-weight bookmark instead. See :hg:`help glossary` for more
776 information about named branches and bookmarks.
776 information about named branches and bookmarks.
777
777
778 Returns 0 on success.
778 Returns 0 on success.
779 """
779 """
780
780
781 if opts.get('clean'):
781 if opts.get('clean'):
782 label = repo[None].p1().branch()
782 label = repo[None].p1().branch()
783 repo.dirstate.setbranch(label)
783 repo.dirstate.setbranch(label)
784 ui.status(_('reset working directory to branch %s\n') % label)
784 ui.status(_('reset working directory to branch %s\n') % label)
785 elif label:
785 elif label:
786 if not opts.get('force') and label in repo.branchtags():
786 if not opts.get('force') and label in repo.branchtags():
787 if label not in [p.branch() for p in repo.parents()]:
787 if label not in [p.branch() for p in repo.parents()]:
788 raise util.Abort(_('a branch of the same name already exists'),
788 raise util.Abort(_('a branch of the same name already exists'),
789 # i18n: "it" refers to an existing branch
789 # i18n: "it" refers to an existing branch
790 hint=_("use 'hg update' to switch to it"))
790 hint=_("use 'hg update' to switch to it"))
791 repo.dirstate.setbranch(label)
791 repo.dirstate.setbranch(label)
792 ui.status(_('marked working directory as branch %s\n') % label)
792 ui.status(_('marked working directory as branch %s\n') % label)
793 else:
793 else:
794 ui.write("%s\n" % repo.dirstate.branch())
794 ui.write("%s\n" % repo.dirstate.branch())
795
795
796 @command('branches',
796 @command('branches',
797 [('a', 'active', False, _('show only branches that have unmerged heads')),
797 [('a', 'active', False, _('show only branches that have unmerged heads')),
798 ('c', 'closed', False, _('show normal and closed branches'))],
798 ('c', 'closed', False, _('show normal and closed branches'))],
799 _('[-ac]'))
799 _('[-ac]'))
800 def branches(ui, repo, active=False, closed=False):
800 def branches(ui, repo, active=False, closed=False):
801 """list repository named branches
801 """list repository named branches
802
802
803 List the repository's named branches, indicating which ones are
803 List the repository's named branches, indicating which ones are
804 inactive. If -c/--closed is specified, also list branches which have
804 inactive. If -c/--closed is specified, also list branches which have
805 been marked closed (see :hg:`commit --close-branch`).
805 been marked closed (see :hg:`commit --close-branch`).
806
806
807 If -a/--active is specified, only show active branches. A branch
807 If -a/--active is specified, only show active branches. A branch
808 is considered active if it contains repository heads.
808 is considered active if it contains repository heads.
809
809
810 Use the command :hg:`update` to switch to an existing branch.
810 Use the command :hg:`update` to switch to an existing branch.
811
811
812 Returns 0.
812 Returns 0.
813 """
813 """
814
814
815 hexfunc = ui.debugflag and hex or short
815 hexfunc = ui.debugflag and hex or short
816 activebranches = [repo[n].branch() for n in repo.heads()]
816 activebranches = [repo[n].branch() for n in repo.heads()]
817 def testactive(tag, node):
817 def testactive(tag, node):
818 realhead = tag in activebranches
818 realhead = tag in activebranches
819 open = node in repo.branchheads(tag, closed=False)
819 open = node in repo.branchheads(tag, closed=False)
820 return realhead and open
820 return realhead and open
821 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
821 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
822 for tag, node in repo.branchtags().items()],
822 for tag, node in repo.branchtags().items()],
823 reverse=True)
823 reverse=True)
824
824
825 for isactive, node, tag in branches:
825 for isactive, node, tag in branches:
826 if (not active) or isactive:
826 if (not active) or isactive:
827 if ui.quiet:
827 if ui.quiet:
828 ui.write("%s\n" % tag)
828 ui.write("%s\n" % tag)
829 else:
829 else:
830 hn = repo.lookup(node)
830 hn = repo.lookup(node)
831 if isactive:
831 if isactive:
832 label = 'branches.active'
832 label = 'branches.active'
833 notice = ''
833 notice = ''
834 elif hn not in repo.branchheads(tag, closed=False):
834 elif hn not in repo.branchheads(tag, closed=False):
835 if not closed:
835 if not closed:
836 continue
836 continue
837 label = 'branches.closed'
837 label = 'branches.closed'
838 notice = _(' (closed)')
838 notice = _(' (closed)')
839 else:
839 else:
840 label = 'branches.inactive'
840 label = 'branches.inactive'
841 notice = _(' (inactive)')
841 notice = _(' (inactive)')
842 if tag == repo.dirstate.branch():
842 if tag == repo.dirstate.branch():
843 label = 'branches.current'
843 label = 'branches.current'
844 rev = str(node).rjust(31 - encoding.colwidth(tag))
844 rev = str(node).rjust(31 - encoding.colwidth(tag))
845 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
845 rev = ui.label('%s:%s' % (rev, hexfunc(hn)), 'log.changeset')
846 tag = ui.label(tag, label)
846 tag = ui.label(tag, label)
847 ui.write("%s %s%s\n" % (tag, rev, notice))
847 ui.write("%s %s%s\n" % (tag, rev, notice))
848
848
849 @command('bundle',
849 @command('bundle',
850 [('f', 'force', None, _('run even when the destination is unrelated')),
850 [('f', 'force', None, _('run even when the destination is unrelated')),
851 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
851 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
852 _('REV')),
852 _('REV')),
853 ('b', 'branch', [], _('a specific branch you would like to bundle'),
853 ('b', 'branch', [], _('a specific branch you would like to bundle'),
854 _('BRANCH')),
854 _('BRANCH')),
855 ('', 'base', [],
855 ('', 'base', [],
856 _('a base changeset assumed to be available at the destination'),
856 _('a base changeset assumed to be available at the destination'),
857 _('REV')),
857 _('REV')),
858 ('a', 'all', None, _('bundle all changesets in the repository')),
858 ('a', 'all', None, _('bundle all changesets in the repository')),
859 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
859 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
860 ] + remoteopts,
860 ] + remoteopts,
861 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
861 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
862 def bundle(ui, repo, fname, dest=None, **opts):
862 def bundle(ui, repo, fname, dest=None, **opts):
863 """create a changegroup file
863 """create a changegroup file
864
864
865 Generate a compressed changegroup file collecting changesets not
865 Generate a compressed changegroup file collecting changesets not
866 known to be in another repository.
866 known to be in another repository.
867
867
868 If you omit the destination repository, then hg assumes the
868 If you omit the destination repository, then hg assumes the
869 destination will have all the nodes you specify with --base
869 destination will have all the nodes you specify with --base
870 parameters. To create a bundle containing all changesets, use
870 parameters. To create a bundle containing all changesets, use
871 -a/--all (or --base null).
871 -a/--all (or --base null).
872
872
873 You can change compression method with the -t/--type option.
873 You can change compression method with the -t/--type option.
874 The available compression methods are: none, bzip2, and
874 The available compression methods are: none, bzip2, and
875 gzip (by default, bundles are compressed using bzip2).
875 gzip (by default, bundles are compressed using bzip2).
876
876
877 The bundle file can then be transferred using conventional means
877 The bundle file can then be transferred using conventional means
878 and applied to another repository with the unbundle or pull
878 and applied to another repository with the unbundle or pull
879 command. This is useful when direct push and pull are not
879 command. This is useful when direct push and pull are not
880 available or when exporting an entire repository is undesirable.
880 available or when exporting an entire repository is undesirable.
881
881
882 Applying bundles preserves all changeset contents including
882 Applying bundles preserves all changeset contents including
883 permissions, copy/rename information, and revision history.
883 permissions, copy/rename information, and revision history.
884
884
885 Returns 0 on success, 1 if no changes found.
885 Returns 0 on success, 1 if no changes found.
886 """
886 """
887 revs = None
887 revs = None
888 if 'rev' in opts:
888 if 'rev' in opts:
889 revs = scmutil.revrange(repo, opts['rev'])
889 revs = scmutil.revrange(repo, opts['rev'])
890
890
891 if opts.get('all'):
891 if opts.get('all'):
892 base = ['null']
892 base = ['null']
893 else:
893 else:
894 base = scmutil.revrange(repo, opts.get('base'))
894 base = scmutil.revrange(repo, opts.get('base'))
895 if base:
895 if base:
896 if dest:
896 if dest:
897 raise util.Abort(_("--base is incompatible with specifying "
897 raise util.Abort(_("--base is incompatible with specifying "
898 "a destination"))
898 "a destination"))
899 common = [repo.lookup(rev) for rev in base]
899 common = [repo.lookup(rev) for rev in base]
900 heads = revs and map(repo.lookup, revs) or revs
900 heads = revs and map(repo.lookup, revs) or revs
901 else:
901 else:
902 dest = ui.expandpath(dest or 'default-push', dest or 'default')
902 dest = ui.expandpath(dest or 'default-push', dest or 'default')
903 dest, branches = hg.parseurl(dest, opts.get('branch'))
903 dest, branches = hg.parseurl(dest, opts.get('branch'))
904 other = hg.peer(repo, opts, dest)
904 other = hg.peer(repo, opts, dest)
905 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
905 revs, checkout = hg.addbranchrevs(repo, other, branches, revs)
906 heads = revs and map(repo.lookup, revs) or revs
906 heads = revs and map(repo.lookup, revs) or revs
907 common, outheads = discovery.findcommonoutgoing(repo, other,
907 common, outheads = discovery.findcommonoutgoing(repo, other,
908 onlyheads=heads,
908 onlyheads=heads,
909 force=opts.get('force'))
909 force=opts.get('force'))
910
910
911 cg = repo.getbundle('bundle', common=common, heads=heads)
911 cg = repo.getbundle('bundle', common=common, heads=heads)
912 if not cg:
912 if not cg:
913 ui.status(_("no changes found\n"))
913 ui.status(_("no changes found\n"))
914 return 1
914 return 1
915
915
916 bundletype = opts.get('type', 'bzip2').lower()
916 bundletype = opts.get('type', 'bzip2').lower()
917 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
917 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
918 bundletype = btypes.get(bundletype)
918 bundletype = btypes.get(bundletype)
919 if bundletype not in changegroup.bundletypes:
919 if bundletype not in changegroup.bundletypes:
920 raise util.Abort(_('unknown bundle type specified with --type'))
920 raise util.Abort(_('unknown bundle type specified with --type'))
921
921
922 changegroup.writebundle(cg, fname, bundletype)
922 changegroup.writebundle(cg, fname, bundletype)
923
923
924 @command('cat',
924 @command('cat',
925 [('o', 'output', '',
925 [('o', 'output', '',
926 _('print output to file with formatted name'), _('FORMAT')),
926 _('print output to file with formatted name'), _('FORMAT')),
927 ('r', 'rev', '', _('print the given revision'), _('REV')),
927 ('r', 'rev', '', _('print the given revision'), _('REV')),
928 ('', 'decode', None, _('apply any matching decode filter')),
928 ('', 'decode', None, _('apply any matching decode filter')),
929 ] + walkopts,
929 ] + walkopts,
930 _('[OPTION]... FILE...'))
930 _('[OPTION]... FILE...'))
931 def cat(ui, repo, file1, *pats, **opts):
931 def cat(ui, repo, file1, *pats, **opts):
932 """output the current or given revision of files
932 """output the current or given revision of files
933
933
934 Print the specified files as they were at the given revision. If
934 Print the specified files as they were at the given revision. If
935 no revision is given, the parent of the working directory is used,
935 no revision is given, the parent of the working directory is used,
936 or tip if no revision is checked out.
936 or tip if no revision is checked out.
937
937
938 Output may be to a file, in which case the name of the file is
938 Output may be to a file, in which case the name of the file is
939 given using a format string. The formatting rules are the same as
939 given using a format string. The formatting rules are the same as
940 for the export command, with the following additions:
940 for the export command, with the following additions:
941
941
942 :``%s``: basename of file being printed
942 :``%s``: basename of file being printed
943 :``%d``: dirname of file being printed, or '.' if in repository root
943 :``%d``: dirname of file being printed, or '.' if in repository root
944 :``%p``: root-relative path name of file being printed
944 :``%p``: root-relative path name of file being printed
945
945
946 Returns 0 on success.
946 Returns 0 on success.
947 """
947 """
948 ctx = scmutil.revsingle(repo, opts.get('rev'))
948 ctx = scmutil.revsingle(repo, opts.get('rev'))
949 err = 1
949 err = 1
950 m = scmutil.match(ctx, (file1,) + pats, opts)
950 m = scmutil.match(ctx, (file1,) + pats, opts)
951 for abs in ctx.walk(m):
951 for abs in ctx.walk(m):
952 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
952 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
953 pathname=abs)
953 pathname=abs)
954 data = ctx[abs].data()
954 data = ctx[abs].data()
955 if opts.get('decode'):
955 if opts.get('decode'):
956 data = repo.wwritedata(abs, data)
956 data = repo.wwritedata(abs, data)
957 fp.write(data)
957 fp.write(data)
958 fp.close()
958 fp.close()
959 err = 0
959 err = 0
960 return err
960 return err
961
961
962 @command('^clone',
962 @command('^clone',
963 [('U', 'noupdate', None,
963 [('U', 'noupdate', None,
964 _('the clone will include an empty working copy (only a repository)')),
964 _('the clone will include an empty working copy (only a repository)')),
965 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
965 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
966 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
966 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
967 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
967 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
968 ('', 'pull', None, _('use pull protocol to copy metadata')),
968 ('', 'pull', None, _('use pull protocol to copy metadata')),
969 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
969 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
970 ] + remoteopts,
970 ] + remoteopts,
971 _('[OPTION]... SOURCE [DEST]'))
971 _('[OPTION]... SOURCE [DEST]'))
972 def clone(ui, source, dest=None, **opts):
972 def clone(ui, source, dest=None, **opts):
973 """make a copy of an existing repository
973 """make a copy of an existing repository
974
974
975 Create a copy of an existing repository in a new directory.
975 Create a copy of an existing repository in a new directory.
976
976
977 If no destination directory name is specified, it defaults to the
977 If no destination directory name is specified, it defaults to the
978 basename of the source.
978 basename of the source.
979
979
980 The location of the source is added to the new repository's
980 The location of the source is added to the new repository's
981 ``.hg/hgrc`` file, as the default to be used for future pulls.
981 ``.hg/hgrc`` file, as the default to be used for future pulls.
982
982
983 See :hg:`help urls` for valid source format details.
983 See :hg:`help urls` for valid source format details.
984
984
985 It is possible to specify an ``ssh://`` URL as the destination, but no
985 It is possible to specify an ``ssh://`` URL as the destination, but no
986 ``.hg/hgrc`` and working directory will be created on the remote side.
986 ``.hg/hgrc`` and working directory will be created on the remote side.
987 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
987 Please see :hg:`help urls` for important details about ``ssh://`` URLs.
988
988
989 A set of changesets (tags, or branch names) to pull may be specified
989 A set of changesets (tags, or branch names) to pull may be specified
990 by listing each changeset (tag, or branch name) with -r/--rev.
990 by listing each changeset (tag, or branch name) with -r/--rev.
991 If -r/--rev is used, the cloned repository will contain only a subset
991 If -r/--rev is used, the cloned repository will contain only a subset
992 of the changesets of the source repository. Only the set of changesets
992 of the changesets of the source repository. Only the set of changesets
993 defined by all -r/--rev options (including all their ancestors)
993 defined by all -r/--rev options (including all their ancestors)
994 will be pulled into the destination repository.
994 will be pulled into the destination repository.
995 No subsequent changesets (including subsequent tags) will be present
995 No subsequent changesets (including subsequent tags) will be present
996 in the destination.
996 in the destination.
997
997
998 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
998 Using -r/--rev (or 'clone src#rev dest') implies --pull, even for
999 local source repositories.
999 local source repositories.
1000
1000
1001 For efficiency, hardlinks are used for cloning whenever the source
1001 For efficiency, hardlinks are used for cloning whenever the source
1002 and destination are on the same filesystem (note this applies only
1002 and destination are on the same filesystem (note this applies only
1003 to the repository data, not to the working directory). Some
1003 to the repository data, not to the working directory). Some
1004 filesystems, such as AFS, implement hardlinking incorrectly, but
1004 filesystems, such as AFS, implement hardlinking incorrectly, but
1005 do not report errors. In these cases, use the --pull option to
1005 do not report errors. In these cases, use the --pull option to
1006 avoid hardlinking.
1006 avoid hardlinking.
1007
1007
1008 In some cases, you can clone repositories and the working directory
1008 In some cases, you can clone repositories and the working directory
1009 using full hardlinks with ::
1009 using full hardlinks with ::
1010
1010
1011 $ cp -al REPO REPOCLONE
1011 $ cp -al REPO REPOCLONE
1012
1012
1013 This is the fastest way to clone, but it is not always safe. The
1013 This is the fastest way to clone, but it is not always safe. The
1014 operation is not atomic (making sure REPO is not modified during
1014 operation is not atomic (making sure REPO is not modified during
1015 the operation is up to you) and you have to make sure your editor
1015 the operation is up to you) and you have to make sure your editor
1016 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
1016 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
1017 this is not compatible with certain extensions that place their
1017 this is not compatible with certain extensions that place their
1018 metadata under the .hg directory, such as mq.
1018 metadata under the .hg directory, such as mq.
1019
1019
1020 Mercurial will update the working directory to the first applicable
1020 Mercurial will update the working directory to the first applicable
1021 revision from this list:
1021 revision from this list:
1022
1022
1023 a) null if -U or the source repository has no changesets
1023 a) null if -U or the source repository has no changesets
1024 b) if -u . and the source repository is local, the first parent of
1024 b) if -u . and the source repository is local, the first parent of
1025 the source repository's working directory
1025 the source repository's working directory
1026 c) the changeset specified with -u (if a branch name, this means the
1026 c) the changeset specified with -u (if a branch name, this means the
1027 latest head of that branch)
1027 latest head of that branch)
1028 d) the changeset specified with -r
1028 d) the changeset specified with -r
1029 e) the tipmost head specified with -b
1029 e) the tipmost head specified with -b
1030 f) the tipmost head specified with the url#branch source syntax
1030 f) the tipmost head specified with the url#branch source syntax
1031 g) the tipmost head of the default branch
1031 g) the tipmost head of the default branch
1032 h) tip
1032 h) tip
1033
1033
1034 Returns 0 on success.
1034 Returns 0 on success.
1035 """
1035 """
1036 if opts.get('noupdate') and opts.get('updaterev'):
1036 if opts.get('noupdate') and opts.get('updaterev'):
1037 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1037 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1038
1038
1039 r = hg.clone(ui, opts, source, dest,
1039 r = hg.clone(ui, opts, source, dest,
1040 pull=opts.get('pull'),
1040 pull=opts.get('pull'),
1041 stream=opts.get('uncompressed'),
1041 stream=opts.get('uncompressed'),
1042 rev=opts.get('rev'),
1042 rev=opts.get('rev'),
1043 update=opts.get('updaterev') or not opts.get('noupdate'),
1043 update=opts.get('updaterev') or not opts.get('noupdate'),
1044 branch=opts.get('branch'))
1044 branch=opts.get('branch'))
1045
1045
1046 return r is None
1046 return r is None
1047
1047
1048 @command('^commit|ci',
1048 @command('^commit|ci',
1049 [('A', 'addremove', None,
1049 [('A', 'addremove', None,
1050 _('mark new/missing files as added/removed before committing')),
1050 _('mark new/missing files as added/removed before committing')),
1051 ('', 'close-branch', None,
1051 ('', 'close-branch', None,
1052 _('mark a branch as closed, hiding it from the branch list')),
1052 _('mark a branch as closed, hiding it from the branch list')),
1053 ] + walkopts + commitopts + commitopts2,
1053 ] + walkopts + commitopts + commitopts2,
1054 _('[OPTION]... [FILE]...'))
1054 _('[OPTION]... [FILE]...'))
1055 def commit(ui, repo, *pats, **opts):
1055 def commit(ui, repo, *pats, **opts):
1056 """commit the specified files or all outstanding changes
1056 """commit the specified files or all outstanding changes
1057
1057
1058 Commit changes to the given files into the repository. Unlike a
1058 Commit changes to the given files into the repository. Unlike a
1059 centralized SCM, this operation is a local operation. See
1059 centralized SCM, this operation is a local operation. See
1060 :hg:`push` for a way to actively distribute your changes.
1060 :hg:`push` for a way to actively distribute your changes.
1061
1061
1062 If a list of files is omitted, all changes reported by :hg:`status`
1062 If a list of files is omitted, all changes reported by :hg:`status`
1063 will be committed.
1063 will be committed.
1064
1064
1065 If you are committing the result of a merge, do not provide any
1065 If you are committing the result of a merge, do not provide any
1066 filenames or -I/-X filters.
1066 filenames or -I/-X filters.
1067
1067
1068 If no commit message is specified, Mercurial starts your
1068 If no commit message is specified, Mercurial starts your
1069 configured editor where you can enter a message. In case your
1069 configured editor where you can enter a message. In case your
1070 commit fails, you will find a backup of your message in
1070 commit fails, you will find a backup of your message in
1071 ``.hg/last-message.txt``.
1071 ``.hg/last-message.txt``.
1072
1072
1073 See :hg:`help dates` for a list of formats valid for -d/--date.
1073 See :hg:`help dates` for a list of formats valid for -d/--date.
1074
1074
1075 Returns 0 on success, 1 if nothing changed.
1075 Returns 0 on success, 1 if nothing changed.
1076 """
1076 """
1077 extra = {}
1077 extra = {}
1078 if opts.get('close_branch'):
1078 if opts.get('close_branch'):
1079 if repo['.'].node() not in repo.branchheads():
1079 if repo['.'].node() not in repo.branchheads():
1080 # The topo heads set is included in the branch heads set of the
1080 # The topo heads set is included in the branch heads set of the
1081 # current branch, so it's sufficient to test branchheads
1081 # current branch, so it's sufficient to test branchheads
1082 raise util.Abort(_('can only close branch heads'))
1082 raise util.Abort(_('can only close branch heads'))
1083 extra['close'] = 1
1083 extra['close'] = 1
1084 e = cmdutil.commiteditor
1084 e = cmdutil.commiteditor
1085 if opts.get('force_editor'):
1085 if opts.get('force_editor'):
1086 e = cmdutil.commitforceeditor
1086 e = cmdutil.commitforceeditor
1087
1087
1088 def commitfunc(ui, repo, message, match, opts):
1088 def commitfunc(ui, repo, message, match, opts):
1089 return repo.commit(message, opts.get('user'), opts.get('date'), match,
1089 return repo.commit(message, opts.get('user'), opts.get('date'), match,
1090 editor=e, extra=extra)
1090 editor=e, extra=extra)
1091
1091
1092 branch = repo[None].branch()
1092 branch = repo[None].branch()
1093 bheads = repo.branchheads(branch)
1093 bheads = repo.branchheads(branch)
1094
1094
1095 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1095 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1096 if not node:
1096 if not node:
1097 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1097 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1098 if stat[3]:
1098 if stat[3]:
1099 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
1099 ui.status(_("nothing changed (%d missing files, see 'hg status')\n")
1100 % len(stat[3]))
1100 % len(stat[3]))
1101 else:
1101 else:
1102 ui.status(_("nothing changed\n"))
1102 ui.status(_("nothing changed\n"))
1103 return 1
1103 return 1
1104
1104
1105 ctx = repo[node]
1105 ctx = repo[node]
1106 parents = ctx.parents()
1106 parents = ctx.parents()
1107
1107
1108 if (bheads and node not in bheads and not
1108 if (bheads and node not in bheads and not
1109 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1109 [x for x in parents if x.node() in bheads and x.branch() == branch]):
1110 ui.status(_('created new head\n'))
1110 ui.status(_('created new head\n'))
1111 # The message is not printed for initial roots. For the other
1111 # The message is not printed for initial roots. For the other
1112 # changesets, it is printed in the following situations:
1112 # changesets, it is printed in the following situations:
1113 #
1113 #
1114 # Par column: for the 2 parents with ...
1114 # Par column: for the 2 parents with ...
1115 # N: null or no parent
1115 # N: null or no parent
1116 # B: parent is on another named branch
1116 # B: parent is on another named branch
1117 # C: parent is a regular non head changeset
1117 # C: parent is a regular non head changeset
1118 # H: parent was a branch head of the current branch
1118 # H: parent was a branch head of the current branch
1119 # Msg column: whether we print "created new head" message
1119 # Msg column: whether we print "created new head" message
1120 # In the following, it is assumed that there already exists some
1120 # In the following, it is assumed that there already exists some
1121 # initial branch heads of the current branch, otherwise nothing is
1121 # initial branch heads of the current branch, otherwise nothing is
1122 # printed anyway.
1122 # printed anyway.
1123 #
1123 #
1124 # Par Msg Comment
1124 # Par Msg Comment
1125 # NN y additional topo root
1125 # NN y additional topo root
1126 #
1126 #
1127 # BN y additional branch root
1127 # BN y additional branch root
1128 # CN y additional topo head
1128 # CN y additional topo head
1129 # HN n usual case
1129 # HN n usual case
1130 #
1130 #
1131 # BB y weird additional branch root
1131 # BB y weird additional branch root
1132 # CB y branch merge
1132 # CB y branch merge
1133 # HB n merge with named branch
1133 # HB n merge with named branch
1134 #
1134 #
1135 # CC y additional head from merge
1135 # CC y additional head from merge
1136 # CH n merge with a head
1136 # CH n merge with a head
1137 #
1137 #
1138 # HH n head merge: head count decreases
1138 # HH n head merge: head count decreases
1139
1139
1140 if not opts.get('close_branch'):
1140 if not opts.get('close_branch'):
1141 for r in parents:
1141 for r in parents:
1142 if r.extra().get('close') and r.branch() == branch:
1142 if r.extra().get('close') and r.branch() == branch:
1143 ui.status(_('reopening closed branch head %d\n') % r)
1143 ui.status(_('reopening closed branch head %d\n') % r)
1144
1144
1145 if ui.debugflag:
1145 if ui.debugflag:
1146 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1146 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx.hex()))
1147 elif ui.verbose:
1147 elif ui.verbose:
1148 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1148 ui.write(_('committed changeset %d:%s\n') % (int(ctx), ctx))
1149
1149
1150 @command('copy|cp',
1150 @command('copy|cp',
1151 [('A', 'after', None, _('record a copy that has already occurred')),
1151 [('A', 'after', None, _('record a copy that has already occurred')),
1152 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1152 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1153 ] + walkopts + dryrunopts,
1153 ] + walkopts + dryrunopts,
1154 _('[OPTION]... [SOURCE]... DEST'))
1154 _('[OPTION]... [SOURCE]... DEST'))
1155 def copy(ui, repo, *pats, **opts):
1155 def copy(ui, repo, *pats, **opts):
1156 """mark files as copied for the next commit
1156 """mark files as copied for the next commit
1157
1157
1158 Mark dest as having copies of source files. If dest is a
1158 Mark dest as having copies of source files. If dest is a
1159 directory, copies are put in that directory. If dest is a file,
1159 directory, copies are put in that directory. If dest is a file,
1160 the source must be a single file.
1160 the source must be a single file.
1161
1161
1162 By default, this command copies the contents of files as they
1162 By default, this command copies the contents of files as they
1163 exist in the working directory. If invoked with -A/--after, the
1163 exist in the working directory. If invoked with -A/--after, the
1164 operation is recorded, but no copying is performed.
1164 operation is recorded, but no copying is performed.
1165
1165
1166 This command takes effect with the next commit. To undo a copy
1166 This command takes effect with the next commit. To undo a copy
1167 before that, see :hg:`revert`.
1167 before that, see :hg:`revert`.
1168
1168
1169 Returns 0 on success, 1 if errors are encountered.
1169 Returns 0 on success, 1 if errors are encountered.
1170 """
1170 """
1171 wlock = repo.wlock(False)
1171 wlock = repo.wlock(False)
1172 try:
1172 try:
1173 return cmdutil.copy(ui, repo, pats, opts)
1173 return cmdutil.copy(ui, repo, pats, opts)
1174 finally:
1174 finally:
1175 wlock.release()
1175 wlock.release()
1176
1176
1177 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1177 @command('debugancestor', [], _('[INDEX] REV1 REV2'))
1178 def debugancestor(ui, repo, *args):
1178 def debugancestor(ui, repo, *args):
1179 """find the ancestor revision of two revisions in a given index"""
1179 """find the ancestor revision of two revisions in a given index"""
1180 if len(args) == 3:
1180 if len(args) == 3:
1181 index, rev1, rev2 = args
1181 index, rev1, rev2 = args
1182 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1182 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1183 lookup = r.lookup
1183 lookup = r.lookup
1184 elif len(args) == 2:
1184 elif len(args) == 2:
1185 if not repo:
1185 if not repo:
1186 raise util.Abort(_("there is no Mercurial repository here "
1186 raise util.Abort(_("there is no Mercurial repository here "
1187 "(.hg not found)"))
1187 "(.hg not found)"))
1188 rev1, rev2 = args
1188 rev1, rev2 = args
1189 r = repo.changelog
1189 r = repo.changelog
1190 lookup = repo.lookup
1190 lookup = repo.lookup
1191 else:
1191 else:
1192 raise util.Abort(_('either two or three arguments required'))
1192 raise util.Abort(_('either two or three arguments required'))
1193 a = r.ancestor(lookup(rev1), lookup(rev2))
1193 a = r.ancestor(lookup(rev1), lookup(rev2))
1194 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1194 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1195
1195
1196 @command('debugbuilddag',
1196 @command('debugbuilddag',
1197 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1197 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1198 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1198 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1199 ('n', 'new-file', None, _('add new file at each rev'))],
1199 ('n', 'new-file', None, _('add new file at each rev'))],
1200 _('[OPTION]... [TEXT]'))
1200 _('[OPTION]... [TEXT]'))
1201 def debugbuilddag(ui, repo, text=None,
1201 def debugbuilddag(ui, repo, text=None,
1202 mergeable_file=False,
1202 mergeable_file=False,
1203 overwritten_file=False,
1203 overwritten_file=False,
1204 new_file=False):
1204 new_file=False):
1205 """builds a repo with a given DAG from scratch in the current empty repo
1205 """builds a repo with a given DAG from scratch in the current empty repo
1206
1206
1207 The description of the DAG is read from stdin if not given on the
1207 The description of the DAG is read from stdin if not given on the
1208 command line.
1208 command line.
1209
1209
1210 Elements:
1210 Elements:
1211
1211
1212 - "+n" is a linear run of n nodes based on the current default parent
1212 - "+n" is a linear run of n nodes based on the current default parent
1213 - "." is a single node based on the current default parent
1213 - "." is a single node based on the current default parent
1214 - "$" resets the default parent to null (implied at the start);
1214 - "$" resets the default parent to null (implied at the start);
1215 otherwise the default parent is always the last node created
1215 otherwise the default parent is always the last node created
1216 - "<p" sets the default parent to the backref p
1216 - "<p" sets the default parent to the backref p
1217 - "*p" is a fork at parent p, which is a backref
1217 - "*p" is a fork at parent p, which is a backref
1218 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1218 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1219 - "/p2" is a merge of the preceding node and p2
1219 - "/p2" is a merge of the preceding node and p2
1220 - ":tag" defines a local tag for the preceding node
1220 - ":tag" defines a local tag for the preceding node
1221 - "@branch" sets the named branch for subsequent nodes
1221 - "@branch" sets the named branch for subsequent nodes
1222 - "#...\\n" is a comment up to the end of the line
1222 - "#...\\n" is a comment up to the end of the line
1223
1223
1224 Whitespace between the above elements is ignored.
1224 Whitespace between the above elements is ignored.
1225
1225
1226 A backref is either
1226 A backref is either
1227
1227
1228 - a number n, which references the node curr-n, where curr is the current
1228 - a number n, which references the node curr-n, where curr is the current
1229 node, or
1229 node, or
1230 - the name of a local tag you placed earlier using ":tag", or
1230 - the name of a local tag you placed earlier using ":tag", or
1231 - empty to denote the default parent.
1231 - empty to denote the default parent.
1232
1232
1233 All string valued-elements are either strictly alphanumeric, or must
1233 All string valued-elements are either strictly alphanumeric, or must
1234 be enclosed in double quotes ("..."), with "\\" as escape character.
1234 be enclosed in double quotes ("..."), with "\\" as escape character.
1235 """
1235 """
1236
1236
1237 if text is None:
1237 if text is None:
1238 ui.status(_("reading DAG from stdin\n"))
1238 ui.status(_("reading DAG from stdin\n"))
1239 text = ui.fin.read()
1239 text = ui.fin.read()
1240
1240
1241 cl = repo.changelog
1241 cl = repo.changelog
1242 if len(cl) > 0:
1242 if len(cl) > 0:
1243 raise util.Abort(_('repository is not empty'))
1243 raise util.Abort(_('repository is not empty'))
1244
1244
1245 # determine number of revs in DAG
1245 # determine number of revs in DAG
1246 total = 0
1246 total = 0
1247 for type, data in dagparser.parsedag(text):
1247 for type, data in dagparser.parsedag(text):
1248 if type == 'n':
1248 if type == 'n':
1249 total += 1
1249 total += 1
1250
1250
1251 if mergeable_file:
1251 if mergeable_file:
1252 linesperrev = 2
1252 linesperrev = 2
1253 # make a file with k lines per rev
1253 # make a file with k lines per rev
1254 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1254 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1255 initialmergedlines.append("")
1255 initialmergedlines.append("")
1256
1256
1257 tags = []
1257 tags = []
1258
1258
1259 tr = repo.transaction("builddag")
1259 tr = repo.transaction("builddag")
1260 try:
1260 try:
1261
1261
1262 at = -1
1262 at = -1
1263 atbranch = 'default'
1263 atbranch = 'default'
1264 nodeids = []
1264 nodeids = []
1265 ui.progress(_('building'), 0, unit=_('revisions'), total=total)
1265 ui.progress(_('building'), 0, unit=_('revisions'), total=total)
1266 for type, data in dagparser.parsedag(text):
1266 for type, data in dagparser.parsedag(text):
1267 if type == 'n':
1267 if type == 'n':
1268 ui.note('node %s\n' % str(data))
1268 ui.note('node %s\n' % str(data))
1269 id, ps = data
1269 id, ps = data
1270
1270
1271 files = []
1271 files = []
1272 fctxs = {}
1272 fctxs = {}
1273
1273
1274 p2 = None
1274 p2 = None
1275 if mergeable_file:
1275 if mergeable_file:
1276 fn = "mf"
1276 fn = "mf"
1277 p1 = repo[ps[0]]
1277 p1 = repo[ps[0]]
1278 if len(ps) > 1:
1278 if len(ps) > 1:
1279 p2 = repo[ps[1]]
1279 p2 = repo[ps[1]]
1280 pa = p1.ancestor(p2)
1280 pa = p1.ancestor(p2)
1281 base, local, other = [x[fn].data() for x in pa, p1, p2]
1281 base, local, other = [x[fn].data() for x in pa, p1, p2]
1282 m3 = simplemerge.Merge3Text(base, local, other)
1282 m3 = simplemerge.Merge3Text(base, local, other)
1283 ml = [l.strip() for l in m3.merge_lines()]
1283 ml = [l.strip() for l in m3.merge_lines()]
1284 ml.append("")
1284 ml.append("")
1285 elif at > 0:
1285 elif at > 0:
1286 ml = p1[fn].data().split("\n")
1286 ml = p1[fn].data().split("\n")
1287 else:
1287 else:
1288 ml = initialmergedlines
1288 ml = initialmergedlines
1289 ml[id * linesperrev] += " r%i" % id
1289 ml[id * linesperrev] += " r%i" % id
1290 mergedtext = "\n".join(ml)
1290 mergedtext = "\n".join(ml)
1291 files.append(fn)
1291 files.append(fn)
1292 fctxs[fn] = context.memfilectx(fn, mergedtext)
1292 fctxs[fn] = context.memfilectx(fn, mergedtext)
1293
1293
1294 if overwritten_file:
1294 if overwritten_file:
1295 fn = "of"
1295 fn = "of"
1296 files.append(fn)
1296 files.append(fn)
1297 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1297 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1298
1298
1299 if new_file:
1299 if new_file:
1300 fn = "nf%i" % id
1300 fn = "nf%i" % id
1301 files.append(fn)
1301 files.append(fn)
1302 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1302 fctxs[fn] = context.memfilectx(fn, "r%i\n" % id)
1303 if len(ps) > 1:
1303 if len(ps) > 1:
1304 if not p2:
1304 if not p2:
1305 p2 = repo[ps[1]]
1305 p2 = repo[ps[1]]
1306 for fn in p2:
1306 for fn in p2:
1307 if fn.startswith("nf"):
1307 if fn.startswith("nf"):
1308 files.append(fn)
1308 files.append(fn)
1309 fctxs[fn] = p2[fn]
1309 fctxs[fn] = p2[fn]
1310
1310
1311 def fctxfn(repo, cx, path):
1311 def fctxfn(repo, cx, path):
1312 return fctxs.get(path)
1312 return fctxs.get(path)
1313
1313
1314 if len(ps) == 0 or ps[0] < 0:
1314 if len(ps) == 0 or ps[0] < 0:
1315 pars = [None, None]
1315 pars = [None, None]
1316 elif len(ps) == 1:
1316 elif len(ps) == 1:
1317 pars = [nodeids[ps[0]], None]
1317 pars = [nodeids[ps[0]], None]
1318 else:
1318 else:
1319 pars = [nodeids[p] for p in ps]
1319 pars = [nodeids[p] for p in ps]
1320 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1320 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1321 date=(id, 0),
1321 date=(id, 0),
1322 user="debugbuilddag",
1322 user="debugbuilddag",
1323 extra={'branch': atbranch})
1323 extra={'branch': atbranch})
1324 nodeid = repo.commitctx(cx)
1324 nodeid = repo.commitctx(cx)
1325 nodeids.append(nodeid)
1325 nodeids.append(nodeid)
1326 at = id
1326 at = id
1327 elif type == 'l':
1327 elif type == 'l':
1328 id, name = data
1328 id, name = data
1329 ui.note('tag %s\n' % name)
1329 ui.note('tag %s\n' % name)
1330 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1330 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1331 elif type == 'a':
1331 elif type == 'a':
1332 ui.note('branch %s\n' % data)
1332 ui.note('branch %s\n' % data)
1333 atbranch = data
1333 atbranch = data
1334 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1334 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1335 tr.close()
1335 tr.close()
1336 finally:
1336 finally:
1337 ui.progress(_('building'), None)
1337 ui.progress(_('building'), None)
1338 tr.release()
1338 tr.release()
1339
1339
1340 if tags:
1340 if tags:
1341 repo.opener.write("localtags", "".join(tags))
1341 repo.opener.write("localtags", "".join(tags))
1342
1342
1343 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1343 @command('debugbundle', [('a', 'all', None, _('show all details'))], _('FILE'))
1344 def debugbundle(ui, bundlepath, all=None, **opts):
1344 def debugbundle(ui, bundlepath, all=None, **opts):
1345 """lists the contents of a bundle"""
1345 """lists the contents of a bundle"""
1346 f = url.open(ui, bundlepath)
1346 f = url.open(ui, bundlepath)
1347 try:
1347 try:
1348 gen = changegroup.readbundle(f, bundlepath)
1348 gen = changegroup.readbundle(f, bundlepath)
1349 if all:
1349 if all:
1350 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1350 ui.write("format: id, p1, p2, cset, delta base, len(delta)\n")
1351
1351
1352 def showchunks(named):
1352 def showchunks(named):
1353 ui.write("\n%s\n" % named)
1353 ui.write("\n%s\n" % named)
1354 chain = None
1354 chain = None
1355 while True:
1355 while True:
1356 chunkdata = gen.deltachunk(chain)
1356 chunkdata = gen.deltachunk(chain)
1357 if not chunkdata:
1357 if not chunkdata:
1358 break
1358 break
1359 node = chunkdata['node']
1359 node = chunkdata['node']
1360 p1 = chunkdata['p1']
1360 p1 = chunkdata['p1']
1361 p2 = chunkdata['p2']
1361 p2 = chunkdata['p2']
1362 cs = chunkdata['cs']
1362 cs = chunkdata['cs']
1363 deltabase = chunkdata['deltabase']
1363 deltabase = chunkdata['deltabase']
1364 delta = chunkdata['delta']
1364 delta = chunkdata['delta']
1365 ui.write("%s %s %s %s %s %s\n" %
1365 ui.write("%s %s %s %s %s %s\n" %
1366 (hex(node), hex(p1), hex(p2),
1366 (hex(node), hex(p1), hex(p2),
1367 hex(cs), hex(deltabase), len(delta)))
1367 hex(cs), hex(deltabase), len(delta)))
1368 chain = node
1368 chain = node
1369
1369
1370 chunkdata = gen.changelogheader()
1370 chunkdata = gen.changelogheader()
1371 showchunks("changelog")
1371 showchunks("changelog")
1372 chunkdata = gen.manifestheader()
1372 chunkdata = gen.manifestheader()
1373 showchunks("manifest")
1373 showchunks("manifest")
1374 while True:
1374 while True:
1375 chunkdata = gen.filelogheader()
1375 chunkdata = gen.filelogheader()
1376 if not chunkdata:
1376 if not chunkdata:
1377 break
1377 break
1378 fname = chunkdata['filename']
1378 fname = chunkdata['filename']
1379 showchunks(fname)
1379 showchunks(fname)
1380 else:
1380 else:
1381 chunkdata = gen.changelogheader()
1381 chunkdata = gen.changelogheader()
1382 chain = None
1382 chain = None
1383 while True:
1383 while True:
1384 chunkdata = gen.deltachunk(chain)
1384 chunkdata = gen.deltachunk(chain)
1385 if not chunkdata:
1385 if not chunkdata:
1386 break
1386 break
1387 node = chunkdata['node']
1387 node = chunkdata['node']
1388 ui.write("%s\n" % hex(node))
1388 ui.write("%s\n" % hex(node))
1389 chain = node
1389 chain = node
1390 finally:
1390 finally:
1391 f.close()
1391 f.close()
1392
1392
1393 @command('debugcheckstate', [], '')
1393 @command('debugcheckstate', [], '')
1394 def debugcheckstate(ui, repo):
1394 def debugcheckstate(ui, repo):
1395 """validate the correctness of the current dirstate"""
1395 """validate the correctness of the current dirstate"""
1396 parent1, parent2 = repo.dirstate.parents()
1396 parent1, parent2 = repo.dirstate.parents()
1397 m1 = repo[parent1].manifest()
1397 m1 = repo[parent1].manifest()
1398 m2 = repo[parent2].manifest()
1398 m2 = repo[parent2].manifest()
1399 errors = 0
1399 errors = 0
1400 for f in repo.dirstate:
1400 for f in repo.dirstate:
1401 state = repo.dirstate[f]
1401 state = repo.dirstate[f]
1402 if state in "nr" and f not in m1:
1402 if state in "nr" and f not in m1:
1403 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1403 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1404 errors += 1
1404 errors += 1
1405 if state in "a" and f in m1:
1405 if state in "a" and f in m1:
1406 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1406 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1407 errors += 1
1407 errors += 1
1408 if state in "m" and f not in m1 and f not in m2:
1408 if state in "m" and f not in m1 and f not in m2:
1409 ui.warn(_("%s in state %s, but not in either manifest\n") %
1409 ui.warn(_("%s in state %s, but not in either manifest\n") %
1410 (f, state))
1410 (f, state))
1411 errors += 1
1411 errors += 1
1412 for f in m1:
1412 for f in m1:
1413 state = repo.dirstate[f]
1413 state = repo.dirstate[f]
1414 if state not in "nrm":
1414 if state not in "nrm":
1415 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1415 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1416 errors += 1
1416 errors += 1
1417 if errors:
1417 if errors:
1418 error = _(".hg/dirstate inconsistent with current parent's manifest")
1418 error = _(".hg/dirstate inconsistent with current parent's manifest")
1419 raise util.Abort(error)
1419 raise util.Abort(error)
1420
1420
1421 @command('debugcommands', [], _('[COMMAND]'))
1421 @command('debugcommands', [], _('[COMMAND]'))
1422 def debugcommands(ui, cmd='', *args):
1422 def debugcommands(ui, cmd='', *args):
1423 """list all available commands and options"""
1423 """list all available commands and options"""
1424 for cmd, vals in sorted(table.iteritems()):
1424 for cmd, vals in sorted(table.iteritems()):
1425 cmd = cmd.split('|')[0].strip('^')
1425 cmd = cmd.split('|')[0].strip('^')
1426 opts = ', '.join([i[1] for i in vals[1]])
1426 opts = ', '.join([i[1] for i in vals[1]])
1427 ui.write('%s: %s\n' % (cmd, opts))
1427 ui.write('%s: %s\n' % (cmd, opts))
1428
1428
1429 @command('debugcomplete',
1429 @command('debugcomplete',
1430 [('o', 'options', None, _('show the command options'))],
1430 [('o', 'options', None, _('show the command options'))],
1431 _('[-o] CMD'))
1431 _('[-o] CMD'))
1432 def debugcomplete(ui, cmd='', **opts):
1432 def debugcomplete(ui, cmd='', **opts):
1433 """returns the completion list associated with the given command"""
1433 """returns the completion list associated with the given command"""
1434
1434
1435 if opts.get('options'):
1435 if opts.get('options'):
1436 options = []
1436 options = []
1437 otables = [globalopts]
1437 otables = [globalopts]
1438 if cmd:
1438 if cmd:
1439 aliases, entry = cmdutil.findcmd(cmd, table, False)
1439 aliases, entry = cmdutil.findcmd(cmd, table, False)
1440 otables.append(entry[1])
1440 otables.append(entry[1])
1441 for t in otables:
1441 for t in otables:
1442 for o in t:
1442 for o in t:
1443 if "(DEPRECATED)" in o[3]:
1443 if "(DEPRECATED)" in o[3]:
1444 continue
1444 continue
1445 if o[0]:
1445 if o[0]:
1446 options.append('-%s' % o[0])
1446 options.append('-%s' % o[0])
1447 options.append('--%s' % o[1])
1447 options.append('--%s' % o[1])
1448 ui.write("%s\n" % "\n".join(options))
1448 ui.write("%s\n" % "\n".join(options))
1449 return
1449 return
1450
1450
1451 cmdlist = cmdutil.findpossible(cmd, table)
1451 cmdlist = cmdutil.findpossible(cmd, table)
1452 if ui.verbose:
1452 if ui.verbose:
1453 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1453 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1454 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1454 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1455
1455
1456 @command('debugdag',
1456 @command('debugdag',
1457 [('t', 'tags', None, _('use tags as labels')),
1457 [('t', 'tags', None, _('use tags as labels')),
1458 ('b', 'branches', None, _('annotate with branch names')),
1458 ('b', 'branches', None, _('annotate with branch names')),
1459 ('', 'dots', None, _('use dots for runs')),
1459 ('', 'dots', None, _('use dots for runs')),
1460 ('s', 'spaces', None, _('separate elements by spaces'))],
1460 ('s', 'spaces', None, _('separate elements by spaces'))],
1461 _('[OPTION]... [FILE [REV]...]'))
1461 _('[OPTION]... [FILE [REV]...]'))
1462 def debugdag(ui, repo, file_=None, *revs, **opts):
1462 def debugdag(ui, repo, file_=None, *revs, **opts):
1463 """format the changelog or an index DAG as a concise textual description
1463 """format the changelog or an index DAG as a concise textual description
1464
1464
1465 If you pass a revlog index, the revlog's DAG is emitted. If you list
1465 If you pass a revlog index, the revlog's DAG is emitted. If you list
1466 revision numbers, they get labelled in the output as rN.
1466 revision numbers, they get labelled in the output as rN.
1467
1467
1468 Otherwise, the changelog DAG of the current repo is emitted.
1468 Otherwise, the changelog DAG of the current repo is emitted.
1469 """
1469 """
1470 spaces = opts.get('spaces')
1470 spaces = opts.get('spaces')
1471 dots = opts.get('dots')
1471 dots = opts.get('dots')
1472 if file_:
1472 if file_:
1473 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1473 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1474 revs = set((int(r) for r in revs))
1474 revs = set((int(r) for r in revs))
1475 def events():
1475 def events():
1476 for r in rlog:
1476 for r in rlog:
1477 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1477 yield 'n', (r, list(set(p for p in rlog.parentrevs(r) if p != -1)))
1478 if r in revs:
1478 if r in revs:
1479 yield 'l', (r, "r%i" % r)
1479 yield 'l', (r, "r%i" % r)
1480 elif repo:
1480 elif repo:
1481 cl = repo.changelog
1481 cl = repo.changelog
1482 tags = opts.get('tags')
1482 tags = opts.get('tags')
1483 branches = opts.get('branches')
1483 branches = opts.get('branches')
1484 if tags:
1484 if tags:
1485 labels = {}
1485 labels = {}
1486 for l, n in repo.tags().items():
1486 for l, n in repo.tags().items():
1487 labels.setdefault(cl.rev(n), []).append(l)
1487 labels.setdefault(cl.rev(n), []).append(l)
1488 def events():
1488 def events():
1489 b = "default"
1489 b = "default"
1490 for r in cl:
1490 for r in cl:
1491 if branches:
1491 if branches:
1492 newb = cl.read(cl.node(r))[5]['branch']
1492 newb = cl.read(cl.node(r))[5]['branch']
1493 if newb != b:
1493 if newb != b:
1494 yield 'a', newb
1494 yield 'a', newb
1495 b = newb
1495 b = newb
1496 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1496 yield 'n', (r, list(set(p for p in cl.parentrevs(r) if p != -1)))
1497 if tags:
1497 if tags:
1498 ls = labels.get(r)
1498 ls = labels.get(r)
1499 if ls:
1499 if ls:
1500 for l in ls:
1500 for l in ls:
1501 yield 'l', (r, l)
1501 yield 'l', (r, l)
1502 else:
1502 else:
1503 raise util.Abort(_('need repo for changelog dag'))
1503 raise util.Abort(_('need repo for changelog dag'))
1504
1504
1505 for line in dagparser.dagtextlines(events(),
1505 for line in dagparser.dagtextlines(events(),
1506 addspaces=spaces,
1506 addspaces=spaces,
1507 wraplabels=True,
1507 wraplabels=True,
1508 wrapannotations=True,
1508 wrapannotations=True,
1509 wrapnonlinear=dots,
1509 wrapnonlinear=dots,
1510 usedots=dots,
1510 usedots=dots,
1511 maxlinewidth=70):
1511 maxlinewidth=70):
1512 ui.write(line)
1512 ui.write(line)
1513 ui.write("\n")
1513 ui.write("\n")
1514
1514
1515 @command('debugdata',
1515 @command('debugdata',
1516 [('c', 'changelog', False, _('open changelog')),
1516 [('c', 'changelog', False, _('open changelog')),
1517 ('m', 'manifest', False, _('open manifest'))],
1517 ('m', 'manifest', False, _('open manifest'))],
1518 _('-c|-m|FILE REV'))
1518 _('-c|-m|FILE REV'))
1519 def debugdata(ui, repo, file_, rev = None, **opts):
1519 def debugdata(ui, repo, file_, rev = None, **opts):
1520 """dump the contents of a data file revision"""
1520 """dump the contents of a data file revision"""
1521 if opts.get('changelog') or opts.get('manifest'):
1521 if opts.get('changelog') or opts.get('manifest'):
1522 file_, rev = None, file_
1522 file_, rev = None, file_
1523 elif rev is None:
1523 elif rev is None:
1524 raise error.CommandError('debugdata', _('invalid arguments'))
1524 raise error.CommandError('debugdata', _('invalid arguments'))
1525 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1525 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1526 try:
1526 try:
1527 ui.write(r.revision(r.lookup(rev)))
1527 ui.write(r.revision(r.lookup(rev)))
1528 except KeyError:
1528 except KeyError:
1529 raise util.Abort(_('invalid revision identifier %s') % rev)
1529 raise util.Abort(_('invalid revision identifier %s') % rev)
1530
1530
1531 @command('debugdate',
1531 @command('debugdate',
1532 [('e', 'extended', None, _('try extended date formats'))],
1532 [('e', 'extended', None, _('try extended date formats'))],
1533 _('[-e] DATE [RANGE]'))
1533 _('[-e] DATE [RANGE]'))
1534 def debugdate(ui, date, range=None, **opts):
1534 def debugdate(ui, date, range=None, **opts):
1535 """parse and display a date"""
1535 """parse and display a date"""
1536 if opts["extended"]:
1536 if opts["extended"]:
1537 d = util.parsedate(date, util.extendeddateformats)
1537 d = util.parsedate(date, util.extendeddateformats)
1538 else:
1538 else:
1539 d = util.parsedate(date)
1539 d = util.parsedate(date)
1540 ui.write("internal: %s %s\n" % d)
1540 ui.write("internal: %s %s\n" % d)
1541 ui.write("standard: %s\n" % util.datestr(d))
1541 ui.write("standard: %s\n" % util.datestr(d))
1542 if range:
1542 if range:
1543 m = util.matchdate(range)
1543 m = util.matchdate(range)
1544 ui.write("match: %s\n" % m(d[0]))
1544 ui.write("match: %s\n" % m(d[0]))
1545
1545
1546 @command('debugdiscovery',
1546 @command('debugdiscovery',
1547 [('', 'old', None, _('use old-style discovery')),
1547 [('', 'old', None, _('use old-style discovery')),
1548 ('', 'nonheads', None,
1548 ('', 'nonheads', None,
1549 _('use old-style discovery with non-heads included')),
1549 _('use old-style discovery with non-heads included')),
1550 ] + remoteopts,
1550 ] + remoteopts,
1551 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1551 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
1552 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1552 def debugdiscovery(ui, repo, remoteurl="default", **opts):
1553 """runs the changeset discovery protocol in isolation"""
1553 """runs the changeset discovery protocol in isolation"""
1554 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1554 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl), opts.get('branch'))
1555 remote = hg.peer(repo, opts, remoteurl)
1555 remote = hg.peer(repo, opts, remoteurl)
1556 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1556 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
1557
1557
1558 # make sure tests are repeatable
1558 # make sure tests are repeatable
1559 random.seed(12323)
1559 random.seed(12323)
1560
1560
1561 def doit(localheads, remoteheads):
1561 def doit(localheads, remoteheads):
1562 if opts.get('old'):
1562 if opts.get('old'):
1563 if localheads:
1563 if localheads:
1564 raise util.Abort('cannot use localheads with old style discovery')
1564 raise util.Abort('cannot use localheads with old style discovery')
1565 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1565 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
1566 force=True)
1566 force=True)
1567 common = set(common)
1567 common = set(common)
1568 if not opts.get('nonheads'):
1568 if not opts.get('nonheads'):
1569 ui.write("unpruned common: %s\n" % " ".join([short(n)
1569 ui.write("unpruned common: %s\n" % " ".join([short(n)
1570 for n in common]))
1570 for n in common]))
1571 dag = dagutil.revlogdag(repo.changelog)
1571 dag = dagutil.revlogdag(repo.changelog)
1572 all = dag.ancestorset(dag.internalizeall(common))
1572 all = dag.ancestorset(dag.internalizeall(common))
1573 common = dag.externalizeall(dag.headsetofconnecteds(all))
1573 common = dag.externalizeall(dag.headsetofconnecteds(all))
1574 else:
1574 else:
1575 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1575 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
1576 common = set(common)
1576 common = set(common)
1577 rheads = set(hds)
1577 rheads = set(hds)
1578 lheads = set(repo.heads())
1578 lheads = set(repo.heads())
1579 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1579 ui.write("common heads: %s\n" % " ".join([short(n) for n in common]))
1580 if lheads <= common:
1580 if lheads <= common:
1581 ui.write("local is subset\n")
1581 ui.write("local is subset\n")
1582 elif rheads <= common:
1582 elif rheads <= common:
1583 ui.write("remote is subset\n")
1583 ui.write("remote is subset\n")
1584
1584
1585 serverlogs = opts.get('serverlog')
1585 serverlogs = opts.get('serverlog')
1586 if serverlogs:
1586 if serverlogs:
1587 for filename in serverlogs:
1587 for filename in serverlogs:
1588 logfile = open(filename, 'r')
1588 logfile = open(filename, 'r')
1589 try:
1589 try:
1590 line = logfile.readline()
1590 line = logfile.readline()
1591 while line:
1591 while line:
1592 parts = line.strip().split(';')
1592 parts = line.strip().split(';')
1593 op = parts[1]
1593 op = parts[1]
1594 if op == 'cg':
1594 if op == 'cg':
1595 pass
1595 pass
1596 elif op == 'cgss':
1596 elif op == 'cgss':
1597 doit(parts[2].split(' '), parts[3].split(' '))
1597 doit(parts[2].split(' '), parts[3].split(' '))
1598 elif op == 'unb':
1598 elif op == 'unb':
1599 doit(parts[3].split(' '), parts[2].split(' '))
1599 doit(parts[3].split(' '), parts[2].split(' '))
1600 line = logfile.readline()
1600 line = logfile.readline()
1601 finally:
1601 finally:
1602 logfile.close()
1602 logfile.close()
1603
1603
1604 else:
1604 else:
1605 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1605 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
1606 opts.get('remote_head'))
1606 opts.get('remote_head'))
1607 localrevs = opts.get('local_head')
1607 localrevs = opts.get('local_head')
1608 doit(localrevs, remoterevs)
1608 doit(localrevs, remoterevs)
1609
1609
1610 @command('debugfileset', [], ('REVSPEC'))
1610 @command('debugfileset', [], ('REVSPEC'))
1611 def debugfileset(ui, repo, expr):
1611 def debugfileset(ui, repo, expr):
1612 '''parse and apply a fileset specification'''
1612 '''parse and apply a fileset specification'''
1613 if ui.verbose:
1613 if ui.verbose:
1614 tree = fileset.parse(expr)[0]
1614 tree = fileset.parse(expr)[0]
1615 ui.note(tree, "\n")
1615 ui.note(tree, "\n")
1616
1616
1617 for f in fileset.getfileset(repo[None], expr):
1617 for f in fileset.getfileset(repo[None], expr):
1618 ui.write("%s\n" % f)
1618 ui.write("%s\n" % f)
1619
1619
1620 @command('debugfsinfo', [], _('[PATH]'))
1620 @command('debugfsinfo', [], _('[PATH]'))
1621 def debugfsinfo(ui, path = "."):
1621 def debugfsinfo(ui, path = "."):
1622 """show information detected about current filesystem"""
1622 """show information detected about current filesystem"""
1623 util.writefile('.debugfsinfo', '')
1623 util.writefile('.debugfsinfo', '')
1624 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1624 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
1625 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1625 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
1626 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1626 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
1627 and 'yes' or 'no'))
1627 and 'yes' or 'no'))
1628 os.unlink('.debugfsinfo')
1628 os.unlink('.debugfsinfo')
1629
1629
1630 @command('debuggetbundle',
1630 @command('debuggetbundle',
1631 [('H', 'head', [], _('id of head node'), _('ID')),
1631 [('H', 'head', [], _('id of head node'), _('ID')),
1632 ('C', 'common', [], _('id of common node'), _('ID')),
1632 ('C', 'common', [], _('id of common node'), _('ID')),
1633 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1633 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
1634 _('REPO FILE [-H|-C ID]...'))
1634 _('REPO FILE [-H|-C ID]...'))
1635 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1635 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
1636 """retrieves a bundle from a repo
1636 """retrieves a bundle from a repo
1637
1637
1638 Every ID must be a full-length hex node id string. Saves the bundle to the
1638 Every ID must be a full-length hex node id string. Saves the bundle to the
1639 given file.
1639 given file.
1640 """
1640 """
1641 repo = hg.peer(ui, opts, repopath)
1641 repo = hg.peer(ui, opts, repopath)
1642 if not repo.capable('getbundle'):
1642 if not repo.capable('getbundle'):
1643 raise util.Abort("getbundle() not supported by target repository")
1643 raise util.Abort("getbundle() not supported by target repository")
1644 args = {}
1644 args = {}
1645 if common:
1645 if common:
1646 args['common'] = [bin(s) for s in common]
1646 args['common'] = [bin(s) for s in common]
1647 if head:
1647 if head:
1648 args['heads'] = [bin(s) for s in head]
1648 args['heads'] = [bin(s) for s in head]
1649 bundle = repo.getbundle('debug', **args)
1649 bundle = repo.getbundle('debug', **args)
1650
1650
1651 bundletype = opts.get('type', 'bzip2').lower()
1651 bundletype = opts.get('type', 'bzip2').lower()
1652 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1652 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1653 bundletype = btypes.get(bundletype)
1653 bundletype = btypes.get(bundletype)
1654 if bundletype not in changegroup.bundletypes:
1654 if bundletype not in changegroup.bundletypes:
1655 raise util.Abort(_('unknown bundle type specified with --type'))
1655 raise util.Abort(_('unknown bundle type specified with --type'))
1656 changegroup.writebundle(bundle, bundlepath, bundletype)
1656 changegroup.writebundle(bundle, bundlepath, bundletype)
1657
1657
1658 @command('debugignore', [], '')
1658 @command('debugignore', [], '')
1659 def debugignore(ui, repo, *values, **opts):
1659 def debugignore(ui, repo, *values, **opts):
1660 """display the combined ignore pattern"""
1660 """display the combined ignore pattern"""
1661 ignore = repo.dirstate._ignore
1661 ignore = repo.dirstate._ignore
1662 includepat = getattr(ignore, 'includepat', None)
1662 includepat = getattr(ignore, 'includepat', None)
1663 if includepat is not None:
1663 if includepat is not None:
1664 ui.write("%s\n" % includepat)
1664 ui.write("%s\n" % includepat)
1665 else:
1665 else:
1666 raise util.Abort(_("no ignore patterns found"))
1666 raise util.Abort(_("no ignore patterns found"))
1667
1667
1668 @command('debugindex',
1668 @command('debugindex',
1669 [('c', 'changelog', False, _('open changelog')),
1669 [('c', 'changelog', False, _('open changelog')),
1670 ('m', 'manifest', False, _('open manifest')),
1670 ('m', 'manifest', False, _('open manifest')),
1671 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1671 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
1672 _('[-f FORMAT] -c|-m|FILE'))
1672 _('[-f FORMAT] -c|-m|FILE'))
1673 def debugindex(ui, repo, file_ = None, **opts):
1673 def debugindex(ui, repo, file_ = None, **opts):
1674 """dump the contents of an index file"""
1674 """dump the contents of an index file"""
1675 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1675 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
1676 format = opts.get('format', 0)
1676 format = opts.get('format', 0)
1677 if format not in (0, 1):
1677 if format not in (0, 1):
1678 raise util.Abort(_("unknown format %d") % format)
1678 raise util.Abort(_("unknown format %d") % format)
1679
1679
1680 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1680 generaldelta = r.version & revlog.REVLOGGENERALDELTA
1681 if generaldelta:
1681 if generaldelta:
1682 basehdr = ' delta'
1682 basehdr = ' delta'
1683 else:
1683 else:
1684 basehdr = ' base'
1684 basehdr = ' base'
1685
1685
1686 if format == 0:
1686 if format == 0:
1687 ui.write(" rev offset length " + basehdr + " linkrev"
1687 ui.write(" rev offset length " + basehdr + " linkrev"
1688 " nodeid p1 p2\n")
1688 " nodeid p1 p2\n")
1689 elif format == 1:
1689 elif format == 1:
1690 ui.write(" rev flag offset length"
1690 ui.write(" rev flag offset length"
1691 " size " + basehdr + " link p1 p2 nodeid\n")
1691 " size " + basehdr + " link p1 p2 nodeid\n")
1692
1692
1693 for i in r:
1693 for i in r:
1694 node = r.node(i)
1694 node = r.node(i)
1695 if generaldelta:
1695 if generaldelta:
1696 base = r.deltaparent(i)
1696 base = r.deltaparent(i)
1697 else:
1697 else:
1698 base = r.chainbase(i)
1698 base = r.chainbase(i)
1699 if format == 0:
1699 if format == 0:
1700 try:
1700 try:
1701 pp = r.parents(node)
1701 pp = r.parents(node)
1702 except:
1702 except:
1703 pp = [nullid, nullid]
1703 pp = [nullid, nullid]
1704 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1704 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1705 i, r.start(i), r.length(i), base, r.linkrev(i),
1705 i, r.start(i), r.length(i), base, r.linkrev(i),
1706 short(node), short(pp[0]), short(pp[1])))
1706 short(node), short(pp[0]), short(pp[1])))
1707 elif format == 1:
1707 elif format == 1:
1708 pr = r.parentrevs(i)
1708 pr = r.parentrevs(i)
1709 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1709 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
1710 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1710 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
1711 base, r.linkrev(i), pr[0], pr[1], short(node)))
1711 base, r.linkrev(i), pr[0], pr[1], short(node)))
1712
1712
1713 @command('debugindexdot', [], _('FILE'))
1713 @command('debugindexdot', [], _('FILE'))
1714 def debugindexdot(ui, repo, file_):
1714 def debugindexdot(ui, repo, file_):
1715 """dump an index DAG as a graphviz dot file"""
1715 """dump an index DAG as a graphviz dot file"""
1716 r = None
1716 r = None
1717 if repo:
1717 if repo:
1718 filelog = repo.file(file_)
1718 filelog = repo.file(file_)
1719 if len(filelog):
1719 if len(filelog):
1720 r = filelog
1720 r = filelog
1721 if not r:
1721 if not r:
1722 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1722 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1723 ui.write("digraph G {\n")
1723 ui.write("digraph G {\n")
1724 for i in r:
1724 for i in r:
1725 node = r.node(i)
1725 node = r.node(i)
1726 pp = r.parents(node)
1726 pp = r.parents(node)
1727 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1727 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1728 if pp[1] != nullid:
1728 if pp[1] != nullid:
1729 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1729 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1730 ui.write("}\n")
1730 ui.write("}\n")
1731
1731
1732 @command('debuginstall', [], '')
1732 @command('debuginstall', [], '')
1733 def debuginstall(ui):
1733 def debuginstall(ui):
1734 '''test Mercurial installation
1734 '''test Mercurial installation
1735
1735
1736 Returns 0 on success.
1736 Returns 0 on success.
1737 '''
1737 '''
1738
1738
1739 def writetemp(contents):
1739 def writetemp(contents):
1740 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1740 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
1741 f = os.fdopen(fd, "wb")
1741 f = os.fdopen(fd, "wb")
1742 f.write(contents)
1742 f.write(contents)
1743 f.close()
1743 f.close()
1744 return name
1744 return name
1745
1745
1746 problems = 0
1746 problems = 0
1747
1747
1748 # encoding
1748 # encoding
1749 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1749 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
1750 try:
1750 try:
1751 encoding.fromlocal("test")
1751 encoding.fromlocal("test")
1752 except util.Abort, inst:
1752 except util.Abort, inst:
1753 ui.write(" %s\n" % inst)
1753 ui.write(" %s\n" % inst)
1754 ui.write(_(" (check that your locale is properly set)\n"))
1754 ui.write(_(" (check that your locale is properly set)\n"))
1755 problems += 1
1755 problems += 1
1756
1756
1757 # compiled modules
1757 # compiled modules
1758 ui.status(_("Checking installed modules (%s)...\n")
1758 ui.status(_("Checking installed modules (%s)...\n")
1759 % os.path.dirname(__file__))
1759 % os.path.dirname(__file__))
1760 try:
1760 try:
1761 import bdiff, mpatch, base85, osutil
1761 import bdiff, mpatch, base85, osutil
1762 except Exception, inst:
1762 except Exception, inst:
1763 ui.write(" %s\n" % inst)
1763 ui.write(" %s\n" % inst)
1764 ui.write(_(" One or more extensions could not be found"))
1764 ui.write(_(" One or more extensions could not be found"))
1765 ui.write(_(" (check that you compiled the extensions)\n"))
1765 ui.write(_(" (check that you compiled the extensions)\n"))
1766 problems += 1
1766 problems += 1
1767
1767
1768 # templates
1768 # templates
1769 ui.status(_("Checking templates...\n"))
1769 ui.status(_("Checking templates...\n"))
1770 try:
1770 try:
1771 import templater
1771 import templater
1772 templater.templater(templater.templatepath("map-cmdline.default"))
1772 templater.templater(templater.templatepath("map-cmdline.default"))
1773 except Exception, inst:
1773 except Exception, inst:
1774 ui.write(" %s\n" % inst)
1774 ui.write(" %s\n" % inst)
1775 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1775 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
1776 problems += 1
1776 problems += 1
1777
1777
1778 # editor
1778 # editor
1779 ui.status(_("Checking commit editor...\n"))
1779 ui.status(_("Checking commit editor...\n"))
1780 editor = ui.geteditor()
1780 editor = ui.geteditor()
1781 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1781 cmdpath = util.findexe(editor) or util.findexe(editor.split()[0])
1782 if not cmdpath:
1782 if not cmdpath:
1783 if editor == 'vi':
1783 if editor == 'vi':
1784 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1784 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
1785 ui.write(_(" (specify a commit editor in your configuration"
1785 ui.write(_(" (specify a commit editor in your configuration"
1786 " file)\n"))
1786 " file)\n"))
1787 else:
1787 else:
1788 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1788 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
1789 ui.write(_(" (specify a commit editor in your configuration"
1789 ui.write(_(" (specify a commit editor in your configuration"
1790 " file)\n"))
1790 " file)\n"))
1791 problems += 1
1791 problems += 1
1792
1792
1793 # check username
1793 # check username
1794 ui.status(_("Checking username...\n"))
1794 ui.status(_("Checking username...\n"))
1795 try:
1795 try:
1796 ui.username()
1796 ui.username()
1797 except util.Abort, e:
1797 except util.Abort, e:
1798 ui.write(" %s\n" % e)
1798 ui.write(" %s\n" % e)
1799 ui.write(_(" (specify a username in your configuration file)\n"))
1799 ui.write(_(" (specify a username in your configuration file)\n"))
1800 problems += 1
1800 problems += 1
1801
1801
1802 if not problems:
1802 if not problems:
1803 ui.status(_("No problems detected\n"))
1803 ui.status(_("No problems detected\n"))
1804 else:
1804 else:
1805 ui.write(_("%s problems detected,"
1805 ui.write(_("%s problems detected,"
1806 " please check your install!\n") % problems)
1806 " please check your install!\n") % problems)
1807
1807
1808 return problems
1808 return problems
1809
1809
1810 @command('debugknown', [], _('REPO ID...'))
1810 @command('debugknown', [], _('REPO ID...'))
1811 def debugknown(ui, repopath, *ids, **opts):
1811 def debugknown(ui, repopath, *ids, **opts):
1812 """test whether node ids are known to a repo
1812 """test whether node ids are known to a repo
1813
1813
1814 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1814 Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
1815 indicating unknown/known.
1815 indicating unknown/known.
1816 """
1816 """
1817 repo = hg.peer(ui, opts, repopath)
1817 repo = hg.peer(ui, opts, repopath)
1818 if not repo.capable('known'):
1818 if not repo.capable('known'):
1819 raise util.Abort("known() not supported by target repository")
1819 raise util.Abort("known() not supported by target repository")
1820 flags = repo.known([bin(s) for s in ids])
1820 flags = repo.known([bin(s) for s in ids])
1821 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1821 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
1822
1822
1823 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
1823 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'))
1824 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1824 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
1825 '''access the pushkey key/value protocol
1825 '''access the pushkey key/value protocol
1826
1826
1827 With two args, list the keys in the given namespace.
1827 With two args, list the keys in the given namespace.
1828
1828
1829 With five args, set a key to new if it currently is set to old.
1829 With five args, set a key to new if it currently is set to old.
1830 Reports success or failure.
1830 Reports success or failure.
1831 '''
1831 '''
1832
1832
1833 target = hg.peer(ui, {}, repopath)
1833 target = hg.peer(ui, {}, repopath)
1834 if keyinfo:
1834 if keyinfo:
1835 key, old, new = keyinfo
1835 key, old, new = keyinfo
1836 r = target.pushkey(namespace, key, old, new)
1836 r = target.pushkey(namespace, key, old, new)
1837 ui.status(str(r) + '\n')
1837 ui.status(str(r) + '\n')
1838 return not r
1838 return not r
1839 else:
1839 else:
1840 for k, v in target.listkeys(namespace).iteritems():
1840 for k, v in target.listkeys(namespace).iteritems():
1841 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1841 ui.write("%s\t%s\n" % (k.encode('string-escape'),
1842 v.encode('string-escape')))
1842 v.encode('string-escape')))
1843
1843
1844 @command('debugrebuildstate',
1844 @command('debugrebuildstate',
1845 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
1845 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
1846 _('[-r REV] [REV]'))
1846 _('[-r REV] [REV]'))
1847 def debugrebuildstate(ui, repo, rev="tip"):
1847 def debugrebuildstate(ui, repo, rev="tip"):
1848 """rebuild the dirstate as it would look like for the given revision"""
1848 """rebuild the dirstate as it would look like for the given revision"""
1849 ctx = scmutil.revsingle(repo, rev)
1849 ctx = scmutil.revsingle(repo, rev)
1850 wlock = repo.wlock()
1850 wlock = repo.wlock()
1851 try:
1851 try:
1852 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1852 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1853 finally:
1853 finally:
1854 wlock.release()
1854 wlock.release()
1855
1855
1856 @command('debugrename',
1856 @command('debugrename',
1857 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1857 [('r', 'rev', '', _('revision to debug'), _('REV'))],
1858 _('[-r REV] FILE'))
1858 _('[-r REV] FILE'))
1859 def debugrename(ui, repo, file1, *pats, **opts):
1859 def debugrename(ui, repo, file1, *pats, **opts):
1860 """dump rename information"""
1860 """dump rename information"""
1861
1861
1862 ctx = scmutil.revsingle(repo, opts.get('rev'))
1862 ctx = scmutil.revsingle(repo, opts.get('rev'))
1863 m = scmutil.match(ctx, (file1,) + pats, opts)
1863 m = scmutil.match(ctx, (file1,) + pats, opts)
1864 for abs in ctx.walk(m):
1864 for abs in ctx.walk(m):
1865 fctx = ctx[abs]
1865 fctx = ctx[abs]
1866 o = fctx.filelog().renamed(fctx.filenode())
1866 o = fctx.filelog().renamed(fctx.filenode())
1867 rel = m.rel(abs)
1867 rel = m.rel(abs)
1868 if o:
1868 if o:
1869 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1869 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1870 else:
1870 else:
1871 ui.write(_("%s not renamed\n") % rel)
1871 ui.write(_("%s not renamed\n") % rel)
1872
1872
1873 @command('debugrevlog',
1873 @command('debugrevlog',
1874 [('c', 'changelog', False, _('open changelog')),
1874 [('c', 'changelog', False, _('open changelog')),
1875 ('m', 'manifest', False, _('open manifest')),
1875 ('m', 'manifest', False, _('open manifest')),
1876 ('d', 'dump', False, _('dump index data'))],
1876 ('d', 'dump', False, _('dump index data'))],
1877 _('-c|-m|FILE'))
1877 _('-c|-m|FILE'))
1878 def debugrevlog(ui, repo, file_ = None, **opts):
1878 def debugrevlog(ui, repo, file_ = None, **opts):
1879 """show data and statistics about a revlog"""
1879 """show data and statistics about a revlog"""
1880 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1880 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
1881
1881
1882 if opts.get("dump"):
1882 if opts.get("dump"):
1883 numrevs = len(r)
1883 numrevs = len(r)
1884 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
1884 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
1885 " rawsize totalsize compression heads\n")
1885 " rawsize totalsize compression heads\n")
1886 ts = 0
1886 ts = 0
1887 heads = set()
1887 heads = set()
1888 for rev in xrange(numrevs):
1888 for rev in xrange(numrevs):
1889 dbase = r.deltaparent(rev)
1889 dbase = r.deltaparent(rev)
1890 if dbase == -1:
1890 if dbase == -1:
1891 dbase = rev
1891 dbase = rev
1892 cbase = r.chainbase(rev)
1892 cbase = r.chainbase(rev)
1893 p1, p2 = r.parentrevs(rev)
1893 p1, p2 = r.parentrevs(rev)
1894 rs = r.rawsize(rev)
1894 rs = r.rawsize(rev)
1895 ts = ts + rs
1895 ts = ts + rs
1896 heads -= set(r.parentrevs(rev))
1896 heads -= set(r.parentrevs(rev))
1897 heads.add(rev)
1897 heads.add(rev)
1898 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
1898 ui.write("%d %d %d %d %d %d %d %d %d %d %d %d %d\n" %
1899 (rev, p1, p2, r.start(rev), r.end(rev),
1899 (rev, p1, p2, r.start(rev), r.end(rev),
1900 r.start(dbase), r.start(cbase),
1900 r.start(dbase), r.start(cbase),
1901 r.start(p1), r.start(p2),
1901 r.start(p1), r.start(p2),
1902 rs, ts, ts / r.end(rev), len(heads)))
1902 rs, ts, ts / r.end(rev), len(heads)))
1903 return 0
1903 return 0
1904
1904
1905 v = r.version
1905 v = r.version
1906 format = v & 0xFFFF
1906 format = v & 0xFFFF
1907 flags = []
1907 flags = []
1908 gdelta = False
1908 gdelta = False
1909 if v & revlog.REVLOGNGINLINEDATA:
1909 if v & revlog.REVLOGNGINLINEDATA:
1910 flags.append('inline')
1910 flags.append('inline')
1911 if v & revlog.REVLOGGENERALDELTA:
1911 if v & revlog.REVLOGGENERALDELTA:
1912 gdelta = True
1912 gdelta = True
1913 flags.append('generaldelta')
1913 flags.append('generaldelta')
1914 if not flags:
1914 if not flags:
1915 flags = ['(none)']
1915 flags = ['(none)']
1916
1916
1917 nummerges = 0
1917 nummerges = 0
1918 numfull = 0
1918 numfull = 0
1919 numprev = 0
1919 numprev = 0
1920 nump1 = 0
1920 nump1 = 0
1921 nump2 = 0
1921 nump2 = 0
1922 numother = 0
1922 numother = 0
1923 nump1prev = 0
1923 nump1prev = 0
1924 nump2prev = 0
1924 nump2prev = 0
1925 chainlengths = []
1925 chainlengths = []
1926
1926
1927 datasize = [None, 0, 0L]
1927 datasize = [None, 0, 0L]
1928 fullsize = [None, 0, 0L]
1928 fullsize = [None, 0, 0L]
1929 deltasize = [None, 0, 0L]
1929 deltasize = [None, 0, 0L]
1930
1930
1931 def addsize(size, l):
1931 def addsize(size, l):
1932 if l[0] is None or size < l[0]:
1932 if l[0] is None or size < l[0]:
1933 l[0] = size
1933 l[0] = size
1934 if size > l[1]:
1934 if size > l[1]:
1935 l[1] = size
1935 l[1] = size
1936 l[2] += size
1936 l[2] += size
1937
1937
1938 numrevs = len(r)
1938 numrevs = len(r)
1939 for rev in xrange(numrevs):
1939 for rev in xrange(numrevs):
1940 p1, p2 = r.parentrevs(rev)
1940 p1, p2 = r.parentrevs(rev)
1941 delta = r.deltaparent(rev)
1941 delta = r.deltaparent(rev)
1942 if format > 0:
1942 if format > 0:
1943 addsize(r.rawsize(rev), datasize)
1943 addsize(r.rawsize(rev), datasize)
1944 if p2 != nullrev:
1944 if p2 != nullrev:
1945 nummerges += 1
1945 nummerges += 1
1946 size = r.length(rev)
1946 size = r.length(rev)
1947 if delta == nullrev:
1947 if delta == nullrev:
1948 chainlengths.append(0)
1948 chainlengths.append(0)
1949 numfull += 1
1949 numfull += 1
1950 addsize(size, fullsize)
1950 addsize(size, fullsize)
1951 else:
1951 else:
1952 chainlengths.append(chainlengths[delta] + 1)
1952 chainlengths.append(chainlengths[delta] + 1)
1953 addsize(size, deltasize)
1953 addsize(size, deltasize)
1954 if delta == rev - 1:
1954 if delta == rev - 1:
1955 numprev += 1
1955 numprev += 1
1956 if delta == p1:
1956 if delta == p1:
1957 nump1prev += 1
1957 nump1prev += 1
1958 elif delta == p2:
1958 elif delta == p2:
1959 nump2prev += 1
1959 nump2prev += 1
1960 elif delta == p1:
1960 elif delta == p1:
1961 nump1 += 1
1961 nump1 += 1
1962 elif delta == p2:
1962 elif delta == p2:
1963 nump2 += 1
1963 nump2 += 1
1964 elif delta != nullrev:
1964 elif delta != nullrev:
1965 numother += 1
1965 numother += 1
1966
1966
1967 numdeltas = numrevs - numfull
1967 numdeltas = numrevs - numfull
1968 numoprev = numprev - nump1prev - nump2prev
1968 numoprev = numprev - nump1prev - nump2prev
1969 totalrawsize = datasize[2]
1969 totalrawsize = datasize[2]
1970 datasize[2] /= numrevs
1970 datasize[2] /= numrevs
1971 fulltotal = fullsize[2]
1971 fulltotal = fullsize[2]
1972 fullsize[2] /= numfull
1972 fullsize[2] /= numfull
1973 deltatotal = deltasize[2]
1973 deltatotal = deltasize[2]
1974 deltasize[2] /= numrevs - numfull
1974 deltasize[2] /= numrevs - numfull
1975 totalsize = fulltotal + deltatotal
1975 totalsize = fulltotal + deltatotal
1976 avgchainlen = sum(chainlengths) / numrevs
1976 avgchainlen = sum(chainlengths) / numrevs
1977 compratio = totalrawsize / totalsize
1977 compratio = totalrawsize / totalsize
1978
1978
1979 basedfmtstr = '%%%dd\n'
1979 basedfmtstr = '%%%dd\n'
1980 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
1980 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
1981
1981
1982 def dfmtstr(max):
1982 def dfmtstr(max):
1983 return basedfmtstr % len(str(max))
1983 return basedfmtstr % len(str(max))
1984 def pcfmtstr(max, padding=0):
1984 def pcfmtstr(max, padding=0):
1985 return basepcfmtstr % (len(str(max)), ' ' * padding)
1985 return basepcfmtstr % (len(str(max)), ' ' * padding)
1986
1986
1987 def pcfmt(value, total):
1987 def pcfmt(value, total):
1988 return (value, 100 * float(value) / total)
1988 return (value, 100 * float(value) / total)
1989
1989
1990 ui.write('format : %d\n' % format)
1990 ui.write('format : %d\n' % format)
1991 ui.write('flags : %s\n' % ', '.join(flags))
1991 ui.write('flags : %s\n' % ', '.join(flags))
1992
1992
1993 ui.write('\n')
1993 ui.write('\n')
1994 fmt = pcfmtstr(totalsize)
1994 fmt = pcfmtstr(totalsize)
1995 fmt2 = dfmtstr(totalsize)
1995 fmt2 = dfmtstr(totalsize)
1996 ui.write('revisions : ' + fmt2 % numrevs)
1996 ui.write('revisions : ' + fmt2 % numrevs)
1997 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
1997 ui.write(' merges : ' + fmt % pcfmt(nummerges, numrevs))
1998 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
1998 ui.write(' normal : ' + fmt % pcfmt(numrevs - nummerges, numrevs))
1999 ui.write('revisions : ' + fmt2 % numrevs)
1999 ui.write('revisions : ' + fmt2 % numrevs)
2000 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
2000 ui.write(' full : ' + fmt % pcfmt(numfull, numrevs))
2001 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2001 ui.write(' deltas : ' + fmt % pcfmt(numdeltas, numrevs))
2002 ui.write('revision size : ' + fmt2 % totalsize)
2002 ui.write('revision size : ' + fmt2 % totalsize)
2003 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2003 ui.write(' full : ' + fmt % pcfmt(fulltotal, totalsize))
2004 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2004 ui.write(' deltas : ' + fmt % pcfmt(deltatotal, totalsize))
2005
2005
2006 ui.write('\n')
2006 ui.write('\n')
2007 fmt = dfmtstr(max(avgchainlen, compratio))
2007 fmt = dfmtstr(max(avgchainlen, compratio))
2008 ui.write('avg chain length : ' + fmt % avgchainlen)
2008 ui.write('avg chain length : ' + fmt % avgchainlen)
2009 ui.write('compression ratio : ' + fmt % compratio)
2009 ui.write('compression ratio : ' + fmt % compratio)
2010
2010
2011 if format > 0:
2011 if format > 0:
2012 ui.write('\n')
2012 ui.write('\n')
2013 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2013 ui.write('uncompressed data size (min/max/avg) : %d / %d / %d\n'
2014 % tuple(datasize))
2014 % tuple(datasize))
2015 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2015 ui.write('full revision size (min/max/avg) : %d / %d / %d\n'
2016 % tuple(fullsize))
2016 % tuple(fullsize))
2017 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2017 ui.write('delta size (min/max/avg) : %d / %d / %d\n'
2018 % tuple(deltasize))
2018 % tuple(deltasize))
2019
2019
2020 if numdeltas > 0:
2020 if numdeltas > 0:
2021 ui.write('\n')
2021 ui.write('\n')
2022 fmt = pcfmtstr(numdeltas)
2022 fmt = pcfmtstr(numdeltas)
2023 fmt2 = pcfmtstr(numdeltas, 4)
2023 fmt2 = pcfmtstr(numdeltas, 4)
2024 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2024 ui.write('deltas against prev : ' + fmt % pcfmt(numprev, numdeltas))
2025 if numprev > 0:
2025 if numprev > 0:
2026 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
2026 ui.write(' where prev = p1 : ' + fmt2 % pcfmt(nump1prev, numprev))
2027 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
2027 ui.write(' where prev = p2 : ' + fmt2 % pcfmt(nump2prev, numprev))
2028 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
2028 ui.write(' other : ' + fmt2 % pcfmt(numoprev, numprev))
2029 if gdelta:
2029 if gdelta:
2030 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2030 ui.write('deltas against p1 : ' + fmt % pcfmt(nump1, numdeltas))
2031 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2031 ui.write('deltas against p2 : ' + fmt % pcfmt(nump2, numdeltas))
2032 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
2032 ui.write('deltas against other : ' + fmt % pcfmt(numother, numdeltas))
2033
2033
2034 @command('debugrevspec', [], ('REVSPEC'))
2034 @command('debugrevspec', [], ('REVSPEC'))
2035 def debugrevspec(ui, repo, expr):
2035 def debugrevspec(ui, repo, expr):
2036 '''parse and apply a revision specification'''
2036 '''parse and apply a revision specification'''
2037 if ui.verbose:
2037 if ui.verbose:
2038 tree = revset.parse(expr)[0]
2038 tree = revset.parse(expr)[0]
2039 ui.note(tree, "\n")
2039 ui.note(tree, "\n")
2040 newtree = revset.findaliases(ui, tree)
2040 newtree = revset.findaliases(ui, tree)
2041 if newtree != tree:
2041 if newtree != tree:
2042 ui.note(newtree, "\n")
2042 ui.note(newtree, "\n")
2043 func = revset.match(ui, expr)
2043 func = revset.match(ui, expr)
2044 for c in func(repo, range(len(repo))):
2044 for c in func(repo, range(len(repo))):
2045 ui.write("%s\n" % c)
2045 ui.write("%s\n" % c)
2046
2046
2047 @command('debugsetparents', [], _('REV1 [REV2]'))
2047 @command('debugsetparents', [], _('REV1 [REV2]'))
2048 def debugsetparents(ui, repo, rev1, rev2=None):
2048 def debugsetparents(ui, repo, rev1, rev2=None):
2049 """manually set the parents of the current working directory
2049 """manually set the parents of the current working directory
2050
2050
2051 This is useful for writing repository conversion tools, but should
2051 This is useful for writing repository conversion tools, but should
2052 be used with care.
2052 be used with care.
2053
2053
2054 Returns 0 on success.
2054 Returns 0 on success.
2055 """
2055 """
2056
2056
2057 r1 = scmutil.revsingle(repo, rev1).node()
2057 r1 = scmutil.revsingle(repo, rev1).node()
2058 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2058 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2059
2059
2060 wlock = repo.wlock()
2060 wlock = repo.wlock()
2061 try:
2061 try:
2062 repo.dirstate.setparents(r1, r2)
2062 repo.dirstate.setparents(r1, r2)
2063 finally:
2063 finally:
2064 wlock.release()
2064 wlock.release()
2065
2065
2066 @command('debugstate',
2066 @command('debugstate',
2067 [('', 'nodates', None, _('do not display the saved mtime')),
2067 [('', 'nodates', None, _('do not display the saved mtime')),
2068 ('', 'datesort', None, _('sort by saved mtime'))],
2068 ('', 'datesort', None, _('sort by saved mtime'))],
2069 _('[OPTION]...'))
2069 _('[OPTION]...'))
2070 def debugstate(ui, repo, nodates=None, datesort=None):
2070 def debugstate(ui, repo, nodates=None, datesort=None):
2071 """show the contents of the current dirstate"""
2071 """show the contents of the current dirstate"""
2072 timestr = ""
2072 timestr = ""
2073 showdate = not nodates
2073 showdate = not nodates
2074 if datesort:
2074 if datesort:
2075 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2075 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2076 else:
2076 else:
2077 keyfunc = None # sort by filename
2077 keyfunc = None # sort by filename
2078 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2078 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2079 if showdate:
2079 if showdate:
2080 if ent[3] == -1:
2080 if ent[3] == -1:
2081 # Pad or slice to locale representation
2081 # Pad or slice to locale representation
2082 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2082 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2083 time.localtime(0)))
2083 time.localtime(0)))
2084 timestr = 'unset'
2084 timestr = 'unset'
2085 timestr = (timestr[:locale_len] +
2085 timestr = (timestr[:locale_len] +
2086 ' ' * (locale_len - len(timestr)))
2086 ' ' * (locale_len - len(timestr)))
2087 else:
2087 else:
2088 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2088 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2089 time.localtime(ent[3]))
2089 time.localtime(ent[3]))
2090 if ent[1] & 020000:
2090 if ent[1] & 020000:
2091 mode = 'lnk'
2091 mode = 'lnk'
2092 else:
2092 else:
2093 mode = '%3o' % (ent[1] & 0777)
2093 mode = '%3o' % (ent[1] & 0777)
2094 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2094 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2095 for f in repo.dirstate.copies():
2095 for f in repo.dirstate.copies():
2096 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2096 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2097
2097
2098 @command('debugsub',
2098 @command('debugsub',
2099 [('r', 'rev', '',
2099 [('r', 'rev', '',
2100 _('revision to check'), _('REV'))],
2100 _('revision to check'), _('REV'))],
2101 _('[-r REV] [REV]'))
2101 _('[-r REV] [REV]'))
2102 def debugsub(ui, repo, rev=None):
2102 def debugsub(ui, repo, rev=None):
2103 ctx = scmutil.revsingle(repo, rev, None)
2103 ctx = scmutil.revsingle(repo, rev, None)
2104 for k, v in sorted(ctx.substate.items()):
2104 for k, v in sorted(ctx.substate.items()):
2105 ui.write('path %s\n' % k)
2105 ui.write('path %s\n' % k)
2106 ui.write(' source %s\n' % v[0])
2106 ui.write(' source %s\n' % v[0])
2107 ui.write(' revision %s\n' % v[1])
2107 ui.write(' revision %s\n' % v[1])
2108
2108
2109 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2109 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'))
2110 def debugwalk(ui, repo, *pats, **opts):
2110 def debugwalk(ui, repo, *pats, **opts):
2111 """show how files match on given patterns"""
2111 """show how files match on given patterns"""
2112 m = scmutil.match(repo[None], pats, opts)
2112 m = scmutil.match(repo[None], pats, opts)
2113 items = list(repo.walk(m))
2113 items = list(repo.walk(m))
2114 if not items:
2114 if not items:
2115 return
2115 return
2116 fmt = 'f %%-%ds %%-%ds %%s' % (
2116 fmt = 'f %%-%ds %%-%ds %%s' % (
2117 max([len(abs) for abs in items]),
2117 max([len(abs) for abs in items]),
2118 max([len(m.rel(abs)) for abs in items]))
2118 max([len(m.rel(abs)) for abs in items]))
2119 for abs in items:
2119 for abs in items:
2120 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2120 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
2121 ui.write("%s\n" % line.rstrip())
2121 ui.write("%s\n" % line.rstrip())
2122
2122
2123 @command('debugwireargs',
2123 @command('debugwireargs',
2124 [('', 'three', '', 'three'),
2124 [('', 'three', '', 'three'),
2125 ('', 'four', '', 'four'),
2125 ('', 'four', '', 'four'),
2126 ('', 'five', '', 'five'),
2126 ('', 'five', '', 'five'),
2127 ] + remoteopts,
2127 ] + remoteopts,
2128 _('REPO [OPTIONS]... [ONE [TWO]]'))
2128 _('REPO [OPTIONS]... [ONE [TWO]]'))
2129 def debugwireargs(ui, repopath, *vals, **opts):
2129 def debugwireargs(ui, repopath, *vals, **opts):
2130 repo = hg.peer(ui, opts, repopath)
2130 repo = hg.peer(ui, opts, repopath)
2131 for opt in remoteopts:
2131 for opt in remoteopts:
2132 del opts[opt[1]]
2132 del opts[opt[1]]
2133 args = {}
2133 args = {}
2134 for k, v in opts.iteritems():
2134 for k, v in opts.iteritems():
2135 if v:
2135 if v:
2136 args[k] = v
2136 args[k] = v
2137 # run twice to check that we don't mess up the stream for the next command
2137 # run twice to check that we don't mess up the stream for the next command
2138 res1 = repo.debugwireargs(*vals, **args)
2138 res1 = repo.debugwireargs(*vals, **args)
2139 res2 = repo.debugwireargs(*vals, **args)
2139 res2 = repo.debugwireargs(*vals, **args)
2140 ui.write("%s\n" % res1)
2140 ui.write("%s\n" % res1)
2141 if res1 != res2:
2141 if res1 != res2:
2142 ui.warn("%s\n" % res2)
2142 ui.warn("%s\n" % res2)
2143
2143
2144 @command('^diff',
2144 @command('^diff',
2145 [('r', 'rev', [], _('revision'), _('REV')),
2145 [('r', 'rev', [], _('revision'), _('REV')),
2146 ('c', 'change', '', _('change made by revision'), _('REV'))
2146 ('c', 'change', '', _('change made by revision'), _('REV'))
2147 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2147 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2148 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2148 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'))
2149 def diff(ui, repo, *pats, **opts):
2149 def diff(ui, repo, *pats, **opts):
2150 """diff repository (or selected files)
2150 """diff repository (or selected files)
2151
2151
2152 Show differences between revisions for the specified files.
2152 Show differences between revisions for the specified files.
2153
2153
2154 Differences between files are shown using the unified diff format.
2154 Differences between files are shown using the unified diff format.
2155
2155
2156 .. note::
2156 .. note::
2157 diff may generate unexpected results for merges, as it will
2157 diff may generate unexpected results for merges, as it will
2158 default to comparing against the working directory's first
2158 default to comparing against the working directory's first
2159 parent changeset if no revisions are specified.
2159 parent changeset if no revisions are specified.
2160
2160
2161 When two revision arguments are given, then changes are shown
2161 When two revision arguments are given, then changes are shown
2162 between those revisions. If only one revision is specified then
2162 between those revisions. If only one revision is specified then
2163 that revision is compared to the working directory, and, when no
2163 that revision is compared to the working directory, and, when no
2164 revisions are specified, the working directory files are compared
2164 revisions are specified, the working directory files are compared
2165 to its parent.
2165 to its parent.
2166
2166
2167 Alternatively you can specify -c/--change with a revision to see
2167 Alternatively you can specify -c/--change with a revision to see
2168 the changes in that changeset relative to its first parent.
2168 the changes in that changeset relative to its first parent.
2169
2169
2170 Without the -a/--text option, diff will avoid generating diffs of
2170 Without the -a/--text option, diff will avoid generating diffs of
2171 files it detects as binary. With -a, diff will generate a diff
2171 files it detects as binary. With -a, diff will generate a diff
2172 anyway, probably with undesirable results.
2172 anyway, probably with undesirable results.
2173
2173
2174 Use the -g/--git option to generate diffs in the git extended diff
2174 Use the -g/--git option to generate diffs in the git extended diff
2175 format. For more information, read :hg:`help diffs`.
2175 format. For more information, read :hg:`help diffs`.
2176
2176
2177 Returns 0 on success.
2177 Returns 0 on success.
2178 """
2178 """
2179
2179
2180 revs = opts.get('rev')
2180 revs = opts.get('rev')
2181 change = opts.get('change')
2181 change = opts.get('change')
2182 stat = opts.get('stat')
2182 stat = opts.get('stat')
2183 reverse = opts.get('reverse')
2183 reverse = opts.get('reverse')
2184
2184
2185 if revs and change:
2185 if revs and change:
2186 msg = _('cannot specify --rev and --change at the same time')
2186 msg = _('cannot specify --rev and --change at the same time')
2187 raise util.Abort(msg)
2187 raise util.Abort(msg)
2188 elif change:
2188 elif change:
2189 node2 = scmutil.revsingle(repo, change, None).node()
2189 node2 = scmutil.revsingle(repo, change, None).node()
2190 node1 = repo[node2].p1().node()
2190 node1 = repo[node2].p1().node()
2191 else:
2191 else:
2192 node1, node2 = scmutil.revpair(repo, revs)
2192 node1, node2 = scmutil.revpair(repo, revs)
2193
2193
2194 if reverse:
2194 if reverse:
2195 node1, node2 = node2, node1
2195 node1, node2 = node2, node1
2196
2196
2197 diffopts = patch.diffopts(ui, opts)
2197 diffopts = patch.diffopts(ui, opts)
2198 m = scmutil.match(repo[node2], pats, opts)
2198 m = scmutil.match(repo[node2], pats, opts)
2199 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2199 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
2200 listsubrepos=opts.get('subrepos'))
2200 listsubrepos=opts.get('subrepos'))
2201
2201
2202 @command('^export',
2202 @command('^export',
2203 [('o', 'output', '',
2203 [('o', 'output', '',
2204 _('print output to file with formatted name'), _('FORMAT')),
2204 _('print output to file with formatted name'), _('FORMAT')),
2205 ('', 'switch-parent', None, _('diff against the second parent')),
2205 ('', 'switch-parent', None, _('diff against the second parent')),
2206 ('r', 'rev', [], _('revisions to export'), _('REV')),
2206 ('r', 'rev', [], _('revisions to export'), _('REV')),
2207 ] + diffopts,
2207 ] + diffopts,
2208 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2208 _('[OPTION]... [-o OUTFILESPEC] REV...'))
2209 def export(ui, repo, *changesets, **opts):
2209 def export(ui, repo, *changesets, **opts):
2210 """dump the header and diffs for one or more changesets
2210 """dump the header and diffs for one or more changesets
2211
2211
2212 Print the changeset header and diffs for one or more revisions.
2212 Print the changeset header and diffs for one or more revisions.
2213
2213
2214 The information shown in the changeset header is: author, date,
2214 The information shown in the changeset header is: author, date,
2215 branch name (if non-default), changeset hash, parent(s) and commit
2215 branch name (if non-default), changeset hash, parent(s) and commit
2216 comment.
2216 comment.
2217
2217
2218 .. note::
2218 .. note::
2219 export may generate unexpected diff output for merge
2219 export may generate unexpected diff output for merge
2220 changesets, as it will compare the merge changeset against its
2220 changesets, as it will compare the merge changeset against its
2221 first parent only.
2221 first parent only.
2222
2222
2223 Output may be to a file, in which case the name of the file is
2223 Output may be to a file, in which case the name of the file is
2224 given using a format string. The formatting rules are as follows:
2224 given using a format string. The formatting rules are as follows:
2225
2225
2226 :``%%``: literal "%" character
2226 :``%%``: literal "%" character
2227 :``%H``: changeset hash (40 hexadecimal digits)
2227 :``%H``: changeset hash (40 hexadecimal digits)
2228 :``%N``: number of patches being generated
2228 :``%N``: number of patches being generated
2229 :``%R``: changeset revision number
2229 :``%R``: changeset revision number
2230 :``%b``: basename of the exporting repository
2230 :``%b``: basename of the exporting repository
2231 :``%h``: short-form changeset hash (12 hexadecimal digits)
2231 :``%h``: short-form changeset hash (12 hexadecimal digits)
2232 :``%m``: first line of the commit message (only alphanumeric characters)
2232 :``%m``: first line of the commit message (only alphanumeric characters)
2233 :``%n``: zero-padded sequence number, starting at 1
2233 :``%n``: zero-padded sequence number, starting at 1
2234 :``%r``: zero-padded changeset revision number
2234 :``%r``: zero-padded changeset revision number
2235
2235
2236 Without the -a/--text option, export will avoid generating diffs
2236 Without the -a/--text option, export will avoid generating diffs
2237 of files it detects as binary. With -a, export will generate a
2237 of files it detects as binary. With -a, export will generate a
2238 diff anyway, probably with undesirable results.
2238 diff anyway, probably with undesirable results.
2239
2239
2240 Use the -g/--git option to generate diffs in the git extended diff
2240 Use the -g/--git option to generate diffs in the git extended diff
2241 format. See :hg:`help diffs` for more information.
2241 format. See :hg:`help diffs` for more information.
2242
2242
2243 With the --switch-parent option, the diff will be against the
2243 With the --switch-parent option, the diff will be against the
2244 second parent. It can be useful to review a merge.
2244 second parent. It can be useful to review a merge.
2245
2245
2246 Returns 0 on success.
2246 Returns 0 on success.
2247 """
2247 """
2248 changesets += tuple(opts.get('rev', []))
2248 changesets += tuple(opts.get('rev', []))
2249 if not changesets:
2249 if not changesets:
2250 raise util.Abort(_("export requires at least one changeset"))
2250 raise util.Abort(_("export requires at least one changeset"))
2251 revs = scmutil.revrange(repo, changesets)
2251 revs = scmutil.revrange(repo, changesets)
2252 if len(revs) > 1:
2252 if len(revs) > 1:
2253 ui.note(_('exporting patches:\n'))
2253 ui.note(_('exporting patches:\n'))
2254 else:
2254 else:
2255 ui.note(_('exporting patch:\n'))
2255 ui.note(_('exporting patch:\n'))
2256 cmdutil.export(repo, revs, template=opts.get('output'),
2256 cmdutil.export(repo, revs, template=opts.get('output'),
2257 switch_parent=opts.get('switch_parent'),
2257 switch_parent=opts.get('switch_parent'),
2258 opts=patch.diffopts(ui, opts))
2258 opts=patch.diffopts(ui, opts))
2259
2259
2260 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2260 @command('^forget', walkopts, _('[OPTION]... FILE...'))
2261 def forget(ui, repo, *pats, **opts):
2261 def forget(ui, repo, *pats, **opts):
2262 """forget the specified files on the next commit
2262 """forget the specified files on the next commit
2263
2263
2264 Mark the specified files so they will no longer be tracked
2264 Mark the specified files so they will no longer be tracked
2265 after the next commit.
2265 after the next commit.
2266
2266
2267 This only removes files from the current branch, not from the
2267 This only removes files from the current branch, not from the
2268 entire project history, and it does not delete them from the
2268 entire project history, and it does not delete them from the
2269 working directory.
2269 working directory.
2270
2270
2271 To undo a forget before the next commit, see :hg:`add`.
2271 To undo a forget before the next commit, see :hg:`add`.
2272
2272
2273 Returns 0 on success.
2273 Returns 0 on success.
2274 """
2274 """
2275
2275
2276 if not pats:
2276 if not pats:
2277 raise util.Abort(_('no files specified'))
2277 raise util.Abort(_('no files specified'))
2278
2278
2279 m = scmutil.match(repo[None], pats, opts)
2279 m = scmutil.match(repo[None], pats, opts)
2280 s = repo.status(match=m, clean=True)
2280 s = repo.status(match=m, clean=True)
2281 forget = sorted(s[0] + s[1] + s[3] + s[6])
2281 forget = sorted(s[0] + s[1] + s[3] + s[6])
2282 errs = 0
2282 errs = 0
2283
2283
2284 for f in m.files():
2284 for f in m.files():
2285 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2285 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2286 if os.path.exists(m.rel(f)):
2286 if os.path.exists(m.rel(f)):
2287 ui.warn(_('not removing %s: file is already untracked\n')
2287 ui.warn(_('not removing %s: file is already untracked\n')
2288 % m.rel(f))
2288 % m.rel(f))
2289 errs = 1
2289 errs = 1
2290
2290
2291 for f in forget:
2291 for f in forget:
2292 if ui.verbose or not m.exact(f):
2292 if ui.verbose or not m.exact(f):
2293 ui.status(_('removing %s\n') % m.rel(f))
2293 ui.status(_('removing %s\n') % m.rel(f))
2294
2294
2295 repo[None].forget(forget)
2295 repo[None].forget(forget)
2296 return errs
2296 return errs
2297
2297
2298 @command('grep',
2298 @command('grep',
2299 [('0', 'print0', None, _('end fields with NUL')),
2299 [('0', 'print0', None, _('end fields with NUL')),
2300 ('', 'all', None, _('print all revisions that match')),
2300 ('', 'all', None, _('print all revisions that match')),
2301 ('a', 'text', None, _('treat all files as text')),
2301 ('a', 'text', None, _('treat all files as text')),
2302 ('f', 'follow', None,
2302 ('f', 'follow', None,
2303 _('follow changeset history,'
2303 _('follow changeset history,'
2304 ' or file history across copies and renames')),
2304 ' or file history across copies and renames')),
2305 ('i', 'ignore-case', None, _('ignore case when matching')),
2305 ('i', 'ignore-case', None, _('ignore case when matching')),
2306 ('l', 'files-with-matches', None,
2306 ('l', 'files-with-matches', None,
2307 _('print only filenames and revisions that match')),
2307 _('print only filenames and revisions that match')),
2308 ('n', 'line-number', None, _('print matching line numbers')),
2308 ('n', 'line-number', None, _('print matching line numbers')),
2309 ('r', 'rev', [],
2309 ('r', 'rev', [],
2310 _('only search files changed within revision range'), _('REV')),
2310 _('only search files changed within revision range'), _('REV')),
2311 ('u', 'user', None, _('list the author (long with -v)')),
2311 ('u', 'user', None, _('list the author (long with -v)')),
2312 ('d', 'date', None, _('list the date (short with -q)')),
2312 ('d', 'date', None, _('list the date (short with -q)')),
2313 ] + walkopts,
2313 ] + walkopts,
2314 _('[OPTION]... PATTERN [FILE]...'))
2314 _('[OPTION]... PATTERN [FILE]...'))
2315 def grep(ui, repo, pattern, *pats, **opts):
2315 def grep(ui, repo, pattern, *pats, **opts):
2316 """search for a pattern in specified files and revisions
2316 """search for a pattern in specified files and revisions
2317
2317
2318 Search revisions of files for a regular expression.
2318 Search revisions of files for a regular expression.
2319
2319
2320 This command behaves differently than Unix grep. It only accepts
2320 This command behaves differently than Unix grep. It only accepts
2321 Python/Perl regexps. It searches repository history, not the
2321 Python/Perl regexps. It searches repository history, not the
2322 working directory. It always prints the revision number in which a
2322 working directory. It always prints the revision number in which a
2323 match appears.
2323 match appears.
2324
2324
2325 By default, grep only prints output for the first revision of a
2325 By default, grep only prints output for the first revision of a
2326 file in which it finds a match. To get it to print every revision
2326 file in which it finds a match. To get it to print every revision
2327 that contains a change in match status ("-" for a match that
2327 that contains a change in match status ("-" for a match that
2328 becomes a non-match, or "+" for a non-match that becomes a match),
2328 becomes a non-match, or "+" for a non-match that becomes a match),
2329 use the --all flag.
2329 use the --all flag.
2330
2330
2331 Returns 0 if a match is found, 1 otherwise.
2331 Returns 0 if a match is found, 1 otherwise.
2332 """
2332 """
2333 reflags = 0
2333 reflags = 0
2334 if opts.get('ignore_case'):
2334 if opts.get('ignore_case'):
2335 reflags |= re.I
2335 reflags |= re.I
2336 try:
2336 try:
2337 regexp = re.compile(pattern, reflags)
2337 regexp = re.compile(pattern, reflags)
2338 except re.error, inst:
2338 except re.error, inst:
2339 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2339 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
2340 return 1
2340 return 1
2341 sep, eol = ':', '\n'
2341 sep, eol = ':', '\n'
2342 if opts.get('print0'):
2342 if opts.get('print0'):
2343 sep = eol = '\0'
2343 sep = eol = '\0'
2344
2344
2345 getfile = util.lrucachefunc(repo.file)
2345 getfile = util.lrucachefunc(repo.file)
2346
2346
2347 def matchlines(body):
2347 def matchlines(body):
2348 begin = 0
2348 begin = 0
2349 linenum = 0
2349 linenum = 0
2350 while True:
2350 while True:
2351 match = regexp.search(body, begin)
2351 match = regexp.search(body, begin)
2352 if not match:
2352 if not match:
2353 break
2353 break
2354 mstart, mend = match.span()
2354 mstart, mend = match.span()
2355 linenum += body.count('\n', begin, mstart) + 1
2355 linenum += body.count('\n', begin, mstart) + 1
2356 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2356 lstart = body.rfind('\n', begin, mstart) + 1 or begin
2357 begin = body.find('\n', mend) + 1 or len(body)
2357 begin = body.find('\n', mend) + 1 or len(body)
2358 lend = begin - 1
2358 lend = begin - 1
2359 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2359 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
2360
2360
2361 class linestate(object):
2361 class linestate(object):
2362 def __init__(self, line, linenum, colstart, colend):
2362 def __init__(self, line, linenum, colstart, colend):
2363 self.line = line
2363 self.line = line
2364 self.linenum = linenum
2364 self.linenum = linenum
2365 self.colstart = colstart
2365 self.colstart = colstart
2366 self.colend = colend
2366 self.colend = colend
2367
2367
2368 def __hash__(self):
2368 def __hash__(self):
2369 return hash((self.linenum, self.line))
2369 return hash((self.linenum, self.line))
2370
2370
2371 def __eq__(self, other):
2371 def __eq__(self, other):
2372 return self.line == other.line
2372 return self.line == other.line
2373
2373
2374 matches = {}
2374 matches = {}
2375 copies = {}
2375 copies = {}
2376 def grepbody(fn, rev, body):
2376 def grepbody(fn, rev, body):
2377 matches[rev].setdefault(fn, [])
2377 matches[rev].setdefault(fn, [])
2378 m = matches[rev][fn]
2378 m = matches[rev][fn]
2379 for lnum, cstart, cend, line in matchlines(body):
2379 for lnum, cstart, cend, line in matchlines(body):
2380 s = linestate(line, lnum, cstart, cend)
2380 s = linestate(line, lnum, cstart, cend)
2381 m.append(s)
2381 m.append(s)
2382
2382
2383 def difflinestates(a, b):
2383 def difflinestates(a, b):
2384 sm = difflib.SequenceMatcher(None, a, b)
2384 sm = difflib.SequenceMatcher(None, a, b)
2385 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2385 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
2386 if tag == 'insert':
2386 if tag == 'insert':
2387 for i in xrange(blo, bhi):
2387 for i in xrange(blo, bhi):
2388 yield ('+', b[i])
2388 yield ('+', b[i])
2389 elif tag == 'delete':
2389 elif tag == 'delete':
2390 for i in xrange(alo, ahi):
2390 for i in xrange(alo, ahi):
2391 yield ('-', a[i])
2391 yield ('-', a[i])
2392 elif tag == 'replace':
2392 elif tag == 'replace':
2393 for i in xrange(alo, ahi):
2393 for i in xrange(alo, ahi):
2394 yield ('-', a[i])
2394 yield ('-', a[i])
2395 for i in xrange(blo, bhi):
2395 for i in xrange(blo, bhi):
2396 yield ('+', b[i])
2396 yield ('+', b[i])
2397
2397
2398 def display(fn, ctx, pstates, states):
2398 def display(fn, ctx, pstates, states):
2399 rev = ctx.rev()
2399 rev = ctx.rev()
2400 datefunc = ui.quiet and util.shortdate or util.datestr
2400 datefunc = ui.quiet and util.shortdate or util.datestr
2401 found = False
2401 found = False
2402 filerevmatches = {}
2402 filerevmatches = {}
2403 def binary():
2403 def binary():
2404 flog = getfile(fn)
2404 flog = getfile(fn)
2405 return util.binary(flog.read(ctx.filenode(fn)))
2405 return util.binary(flog.read(ctx.filenode(fn)))
2406
2406
2407 if opts.get('all'):
2407 if opts.get('all'):
2408 iter = difflinestates(pstates, states)
2408 iter = difflinestates(pstates, states)
2409 else:
2409 else:
2410 iter = [('', l) for l in states]
2410 iter = [('', l) for l in states]
2411 for change, l in iter:
2411 for change, l in iter:
2412 cols = [fn, str(rev)]
2412 cols = [fn, str(rev)]
2413 before, match, after = None, None, None
2413 before, match, after = None, None, None
2414 if opts.get('line_number'):
2414 if opts.get('line_number'):
2415 cols.append(str(l.linenum))
2415 cols.append(str(l.linenum))
2416 if opts.get('all'):
2416 if opts.get('all'):
2417 cols.append(change)
2417 cols.append(change)
2418 if opts.get('user'):
2418 if opts.get('user'):
2419 cols.append(ui.shortuser(ctx.user()))
2419 cols.append(ui.shortuser(ctx.user()))
2420 if opts.get('date'):
2420 if opts.get('date'):
2421 cols.append(datefunc(ctx.date()))
2421 cols.append(datefunc(ctx.date()))
2422 if opts.get('files_with_matches'):
2422 if opts.get('files_with_matches'):
2423 c = (fn, rev)
2423 c = (fn, rev)
2424 if c in filerevmatches:
2424 if c in filerevmatches:
2425 continue
2425 continue
2426 filerevmatches[c] = 1
2426 filerevmatches[c] = 1
2427 else:
2427 else:
2428 before = l.line[:l.colstart]
2428 before = l.line[:l.colstart]
2429 match = l.line[l.colstart:l.colend]
2429 match = l.line[l.colstart:l.colend]
2430 after = l.line[l.colend:]
2430 after = l.line[l.colend:]
2431 ui.write(sep.join(cols))
2431 ui.write(sep.join(cols))
2432 if before is not None:
2432 if before is not None:
2433 if not opts.get('text') and binary():
2433 if not opts.get('text') and binary():
2434 ui.write(sep + " Binary file matches")
2434 ui.write(sep + " Binary file matches")
2435 else:
2435 else:
2436 ui.write(sep + before)
2436 ui.write(sep + before)
2437 ui.write(match, label='grep.match')
2437 ui.write(match, label='grep.match')
2438 ui.write(after)
2438 ui.write(after)
2439 ui.write(eol)
2439 ui.write(eol)
2440 found = True
2440 found = True
2441 return found
2441 return found
2442
2442
2443 skip = {}
2443 skip = {}
2444 revfiles = {}
2444 revfiles = {}
2445 matchfn = scmutil.match(repo[None], pats, opts)
2445 matchfn = scmutil.match(repo[None], pats, opts)
2446 found = False
2446 found = False
2447 follow = opts.get('follow')
2447 follow = opts.get('follow')
2448
2448
2449 def prep(ctx, fns):
2449 def prep(ctx, fns):
2450 rev = ctx.rev()
2450 rev = ctx.rev()
2451 pctx = ctx.p1()
2451 pctx = ctx.p1()
2452 parent = pctx.rev()
2452 parent = pctx.rev()
2453 matches.setdefault(rev, {})
2453 matches.setdefault(rev, {})
2454 matches.setdefault(parent, {})
2454 matches.setdefault(parent, {})
2455 files = revfiles.setdefault(rev, [])
2455 files = revfiles.setdefault(rev, [])
2456 for fn in fns:
2456 for fn in fns:
2457 flog = getfile(fn)
2457 flog = getfile(fn)
2458 try:
2458 try:
2459 fnode = ctx.filenode(fn)
2459 fnode = ctx.filenode(fn)
2460 except error.LookupError:
2460 except error.LookupError:
2461 continue
2461 continue
2462
2462
2463 copied = flog.renamed(fnode)
2463 copied = flog.renamed(fnode)
2464 copy = follow and copied and copied[0]
2464 copy = follow and copied and copied[0]
2465 if copy:
2465 if copy:
2466 copies.setdefault(rev, {})[fn] = copy
2466 copies.setdefault(rev, {})[fn] = copy
2467 if fn in skip:
2467 if fn in skip:
2468 if copy:
2468 if copy:
2469 skip[copy] = True
2469 skip[copy] = True
2470 continue
2470 continue
2471 files.append(fn)
2471 files.append(fn)
2472
2472
2473 if fn not in matches[rev]:
2473 if fn not in matches[rev]:
2474 grepbody(fn, rev, flog.read(fnode))
2474 grepbody(fn, rev, flog.read(fnode))
2475
2475
2476 pfn = copy or fn
2476 pfn = copy or fn
2477 if pfn not in matches[parent]:
2477 if pfn not in matches[parent]:
2478 try:
2478 try:
2479 fnode = pctx.filenode(pfn)
2479 fnode = pctx.filenode(pfn)
2480 grepbody(pfn, parent, flog.read(fnode))
2480 grepbody(pfn, parent, flog.read(fnode))
2481 except error.LookupError:
2481 except error.LookupError:
2482 pass
2482 pass
2483
2483
2484 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2484 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
2485 rev = ctx.rev()
2485 rev = ctx.rev()
2486 parent = ctx.p1().rev()
2486 parent = ctx.p1().rev()
2487 for fn in sorted(revfiles.get(rev, [])):
2487 for fn in sorted(revfiles.get(rev, [])):
2488 states = matches[rev][fn]
2488 states = matches[rev][fn]
2489 copy = copies.get(rev, {}).get(fn)
2489 copy = copies.get(rev, {}).get(fn)
2490 if fn in skip:
2490 if fn in skip:
2491 if copy:
2491 if copy:
2492 skip[copy] = True
2492 skip[copy] = True
2493 continue
2493 continue
2494 pstates = matches.get(parent, {}).get(copy or fn, [])
2494 pstates = matches.get(parent, {}).get(copy or fn, [])
2495 if pstates or states:
2495 if pstates or states:
2496 r = display(fn, ctx, pstates, states)
2496 r = display(fn, ctx, pstates, states)
2497 found = found or r
2497 found = found or r
2498 if r and not opts.get('all'):
2498 if r and not opts.get('all'):
2499 skip[fn] = True
2499 skip[fn] = True
2500 if copy:
2500 if copy:
2501 skip[copy] = True
2501 skip[copy] = True
2502 del matches[rev]
2502 del matches[rev]
2503 del revfiles[rev]
2503 del revfiles[rev]
2504
2504
2505 return not found
2505 return not found
2506
2506
2507 @command('heads',
2507 @command('heads',
2508 [('r', 'rev', '',
2508 [('r', 'rev', '',
2509 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2509 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
2510 ('t', 'topo', False, _('show topological heads only')),
2510 ('t', 'topo', False, _('show topological heads only')),
2511 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2511 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
2512 ('c', 'closed', False, _('show normal and closed branch heads')),
2512 ('c', 'closed', False, _('show normal and closed branch heads')),
2513 ] + templateopts,
2513 ] + templateopts,
2514 _('[-ac] [-r STARTREV] [REV]...'))
2514 _('[-ac] [-r STARTREV] [REV]...'))
2515 def heads(ui, repo, *branchrevs, **opts):
2515 def heads(ui, repo, *branchrevs, **opts):
2516 """show current repository heads or show branch heads
2516 """show current repository heads or show branch heads
2517
2517
2518 With no arguments, show all repository branch heads.
2518 With no arguments, show all repository branch heads.
2519
2519
2520 Repository "heads" are changesets with no child changesets. They are
2520 Repository "heads" are changesets with no child changesets. They are
2521 where development generally takes place and are the usual targets
2521 where development generally takes place and are the usual targets
2522 for update and merge operations. Branch heads are changesets that have
2522 for update and merge operations. Branch heads are changesets that have
2523 no child changeset on the same branch.
2523 no child changeset on the same branch.
2524
2524
2525 If one or more REVs are given, only branch heads on the branches
2525 If one or more REVs are given, only branch heads on the branches
2526 associated with the specified changesets are shown. This means
2526 associated with the specified changesets are shown. This means
2527 that you can use :hg:`heads foo` to see the heads on a branch
2527 that you can use :hg:`heads foo` to see the heads on a branch
2528 named ``foo``.
2528 named ``foo``.
2529
2529
2530 If -c/--closed is specified, also show branch heads marked closed
2530 If -c/--closed is specified, also show branch heads marked closed
2531 (see :hg:`commit --close-branch`).
2531 (see :hg:`commit --close-branch`).
2532
2532
2533 If STARTREV is specified, only those heads that are descendants of
2533 If STARTREV is specified, only those heads that are descendants of
2534 STARTREV will be displayed.
2534 STARTREV will be displayed.
2535
2535
2536 If -t/--topo is specified, named branch mechanics will be ignored and only
2536 If -t/--topo is specified, named branch mechanics will be ignored and only
2537 changesets without children will be shown.
2537 changesets without children will be shown.
2538
2538
2539 Returns 0 if matching heads are found, 1 if not.
2539 Returns 0 if matching heads are found, 1 if not.
2540 """
2540 """
2541
2541
2542 start = None
2542 start = None
2543 if 'rev' in opts:
2543 if 'rev' in opts:
2544 start = scmutil.revsingle(repo, opts['rev'], None).node()
2544 start = scmutil.revsingle(repo, opts['rev'], None).node()
2545
2545
2546 if opts.get('topo'):
2546 if opts.get('topo'):
2547 heads = [repo[h] for h in repo.heads(start)]
2547 heads = [repo[h] for h in repo.heads(start)]
2548 else:
2548 else:
2549 heads = []
2549 heads = []
2550 for branch in repo.branchmap():
2550 for branch in repo.branchmap():
2551 heads += repo.branchheads(branch, start, opts.get('closed'))
2551 heads += repo.branchheads(branch, start, opts.get('closed'))
2552 heads = [repo[h] for h in heads]
2552 heads = [repo[h] for h in heads]
2553
2553
2554 if branchrevs:
2554 if branchrevs:
2555 branches = set(repo[br].branch() for br in branchrevs)
2555 branches = set(repo[br].branch() for br in branchrevs)
2556 heads = [h for h in heads if h.branch() in branches]
2556 heads = [h for h in heads if h.branch() in branches]
2557
2557
2558 if opts.get('active') and branchrevs:
2558 if opts.get('active') and branchrevs:
2559 dagheads = repo.heads(start)
2559 dagheads = repo.heads(start)
2560 heads = [h for h in heads if h.node() in dagheads]
2560 heads = [h for h in heads if h.node() in dagheads]
2561
2561
2562 if branchrevs:
2562 if branchrevs:
2563 haveheads = set(h.branch() for h in heads)
2563 haveheads = set(h.branch() for h in heads)
2564 if branches - haveheads:
2564 if branches - haveheads:
2565 headless = ', '.join(b for b in branches - haveheads)
2565 headless = ', '.join(b for b in branches - haveheads)
2566 msg = _('no open branch heads found on branches %s')
2566 msg = _('no open branch heads found on branches %s')
2567 if opts.get('rev'):
2567 if opts.get('rev'):
2568 msg += _(' (started at %s)' % opts['rev'])
2568 msg += _(' (started at %s)' % opts['rev'])
2569 ui.warn((msg + '\n') % headless)
2569 ui.warn((msg + '\n') % headless)
2570
2570
2571 if not heads:
2571 if not heads:
2572 return 1
2572 return 1
2573
2573
2574 heads = sorted(heads, key=lambda x: -x.rev())
2574 heads = sorted(heads, key=lambda x: -x.rev())
2575 displayer = cmdutil.show_changeset(ui, repo, opts)
2575 displayer = cmdutil.show_changeset(ui, repo, opts)
2576 for ctx in heads:
2576 for ctx in heads:
2577 displayer.show(ctx)
2577 displayer.show(ctx)
2578 displayer.close()
2578 displayer.close()
2579
2579
2580 @command('help',
2580 @command('help',
2581 [('e', 'extension', None, _('show only help for extensions')),
2581 [('e', 'extension', None, _('show only help for extensions')),
2582 ('c', 'command', None, _('show only help for commands'))],
2582 ('c', 'command', None, _('show only help for commands'))],
2583 _('[-ec] [TOPIC]'))
2583 _('[-ec] [TOPIC]'))
2584 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
2584 def help_(ui, name=None, unknowncmd=False, full=True, **opts):
2585 """show help for a given topic or a help overview
2585 """show help for a given topic or a help overview
2586
2586
2587 With no arguments, print a list of commands with short help messages.
2587 With no arguments, print a list of commands with short help messages.
2588
2588
2589 Given a topic, extension, or command name, print help for that
2589 Given a topic, extension, or command name, print help for that
2590 topic.
2590 topic.
2591
2591
2592 Returns 0 if successful.
2592 Returns 0 if successful.
2593 """
2593 """
2594
2594
2595 optlist = []
2595 optlist = []
2596 textwidth = min(ui.termwidth(), 80) - 2
2596 textwidth = min(ui.termwidth(), 80) - 2
2597
2597
2598 # list all option lists
2598 # list all option lists
2599 def opttext(optlist, width):
2599 def opttext(optlist, width):
2600 out = []
2600 out = []
2601 multioccur = False
2601 multioccur = False
2602 for title, options in optlist:
2602 for title, options in optlist:
2603 out.append(("\n%s" % title, None))
2603 out.append(("\n%s" % title, None))
2604 for option in options:
2604 for option in options:
2605 if len(option) == 5:
2605 if len(option) == 5:
2606 shortopt, longopt, default, desc, optlabel = option
2606 shortopt, longopt, default, desc, optlabel = option
2607 else:
2607 else:
2608 shortopt, longopt, default, desc = option
2608 shortopt, longopt, default, desc = option
2609 optlabel = _("VALUE") # default label
2609 optlabel = _("VALUE") # default label
2610
2610
2611 if _("DEPRECATED") in desc and not ui.verbose:
2611 if _("DEPRECATED") in desc and not ui.verbose:
2612 continue
2612 continue
2613 if isinstance(default, list):
2613 if isinstance(default, list):
2614 numqualifier = " %s [+]" % optlabel
2614 numqualifier = " %s [+]" % optlabel
2615 multioccur = True
2615 multioccur = True
2616 elif (default is not None) and not isinstance(default, bool):
2616 elif (default is not None) and not isinstance(default, bool):
2617 numqualifier = " %s" % optlabel
2617 numqualifier = " %s" % optlabel
2618 else:
2618 else:
2619 numqualifier = ""
2619 numqualifier = ""
2620 out.append(("%2s%s" %
2620 out.append(("%2s%s" %
2621 (shortopt and "-%s" % shortopt,
2621 (shortopt and "-%s" % shortopt,
2622 longopt and " --%s%s" %
2622 longopt and " --%s%s" %
2623 (longopt, numqualifier)),
2623 (longopt, numqualifier)),
2624 "%s%s" % (desc,
2624 "%s%s" % (desc,
2625 default
2625 default
2626 and _(" (default: %s)") % default
2626 and _(" (default: %s)") % default
2627 or "")))
2627 or "")))
2628 if multioccur:
2628 if multioccur:
2629 msg = _("\n[+] marked option can be specified multiple times")
2629 msg = _("\n[+] marked option can be specified multiple times")
2630 if ui.verbose and name != 'shortlist':
2630 if ui.verbose and name != 'shortlist':
2631 out.append((msg, None))
2631 out.append((msg, None))
2632 else:
2632 else:
2633 out.insert(-1, (msg, None))
2633 out.insert(-1, (msg, None))
2634
2634
2635 text = ""
2635 text = ""
2636 if out:
2636 if out:
2637 colwidth = encoding.colwidth
2637 colwidth = encoding.colwidth
2638 # normalize: (opt or message, desc or None, width of opt)
2638 # normalize: (opt or message, desc or None, width of opt)
2639 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2639 entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
2640 for opt, desc in out]
2640 for opt, desc in out]
2641 hanging = max([e[2] for e in entries])
2641 hanging = max([e[2] for e in entries])
2642 for opt, desc, width in entries:
2642 for opt, desc, width in entries:
2643 if desc:
2643 if desc:
2644 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2644 initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
2645 hangindent = ' ' * (hanging + 3)
2645 hangindent = ' ' * (hanging + 3)
2646 text += '%s\n' % (util.wrap(desc, width,
2646 text += '%s\n' % (util.wrap(desc, width,
2647 initindent=initindent,
2647 initindent=initindent,
2648 hangindent=hangindent))
2648 hangindent=hangindent))
2649 else:
2649 else:
2650 text += "%s\n" % opt
2650 text += "%s\n" % opt
2651
2651
2652 return text
2652 return text
2653
2653
2654 def addglobalopts(aliases):
2654 def addglobalopts(aliases):
2655 if ui.verbose:
2655 if ui.verbose:
2656 optlist.append((_("global options:"), globalopts))
2656 optlist.append((_("global options:"), globalopts))
2657 if name == 'shortlist':
2657 if name == 'shortlist':
2658 optlist.append((_('use "hg help" for the full list '
2658 optlist.append((_('use "hg help" for the full list '
2659 'of commands'), ()))
2659 'of commands'), ()))
2660 else:
2660 else:
2661 if name == 'shortlist':
2661 if name == 'shortlist':
2662 msg = _('use "hg help" for the full list of commands '
2662 msg = _('use "hg help" for the full list of commands '
2663 'or "hg -v" for details')
2663 'or "hg -v" for details')
2664 elif name and not full:
2664 elif name and not full:
2665 msg = _('use "hg help %s" to show the full help text' % name)
2665 msg = _('use "hg help %s" to show the full help text' % name)
2666 elif aliases:
2666 elif aliases:
2667 msg = _('use "hg -v help%s" to show builtin aliases and '
2667 msg = _('use "hg -v help%s" to show builtin aliases and '
2668 'global options') % (name and " " + name or "")
2668 'global options') % (name and " " + name or "")
2669 else:
2669 else:
2670 msg = _('use "hg -v help %s" to show global options') % name
2670 msg = _('use "hg -v help %s" to show global options') % name
2671 optlist.append((msg, ()))
2671 optlist.append((msg, ()))
2672
2672
2673 def helpcmd(name):
2673 def helpcmd(name):
2674 try:
2674 try:
2675 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
2675 aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
2676 except error.AmbiguousCommand, inst:
2676 except error.AmbiguousCommand, inst:
2677 # py3k fix: except vars can't be used outside the scope of the
2677 # py3k fix: except vars can't be used outside the scope of the
2678 # except block, nor can be used inside a lambda. python issue4617
2678 # except block, nor can be used inside a lambda. python issue4617
2679 prefix = inst.args[0]
2679 prefix = inst.args[0]
2680 select = lambda c: c.lstrip('^').startswith(prefix)
2680 select = lambda c: c.lstrip('^').startswith(prefix)
2681 helplist(_('list of commands:\n\n'), select)
2681 helplist(_('list of commands:\n\n'), select)
2682 return
2682 return
2683
2683
2684 # check if it's an invalid alias and display its error if it is
2684 # check if it's an invalid alias and display its error if it is
2685 if getattr(entry[0], 'badalias', False):
2685 if getattr(entry[0], 'badalias', False):
2686 if not unknowncmd:
2686 if not unknowncmd:
2687 entry[0](ui)
2687 entry[0](ui)
2688 return
2688 return
2689
2689
2690 # synopsis
2690 # synopsis
2691 if len(entry) > 2:
2691 if len(entry) > 2:
2692 if entry[2].startswith('hg'):
2692 if entry[2].startswith('hg'):
2693 ui.write("%s\n" % entry[2])
2693 ui.write("%s\n" % entry[2])
2694 else:
2694 else:
2695 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
2695 ui.write('hg %s %s\n' % (aliases[0], entry[2]))
2696 else:
2696 else:
2697 ui.write('hg %s\n' % aliases[0])
2697 ui.write('hg %s\n' % aliases[0])
2698
2698
2699 # aliases
2699 # aliases
2700 if full and not ui.quiet and len(aliases) > 1:
2700 if full and not ui.quiet and len(aliases) > 1:
2701 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
2701 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
2702
2702
2703 # description
2703 # description
2704 doc = gettext(entry[0].__doc__)
2704 doc = gettext(entry[0].__doc__)
2705 if not doc:
2705 if not doc:
2706 doc = _("(no help text available)")
2706 doc = _("(no help text available)")
2707 if util.safehasattr(entry[0], 'definition'): # aliased command
2707 if util.safehasattr(entry[0], 'definition'): # aliased command
2708 if entry[0].definition.startswith('!'): # shell alias
2708 if entry[0].definition.startswith('!'): # shell alias
2709 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
2709 doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
2710 else:
2710 else:
2711 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
2711 doc = _('alias for: hg %s\n\n%s') % (entry[0].definition, doc)
2712 if ui.quiet or not full:
2712 if ui.quiet or not full:
2713 doc = doc.splitlines()[0]
2713 doc = doc.splitlines()[0]
2714 keep = ui.verbose and ['verbose'] or []
2714 keep = ui.verbose and ['verbose'] or []
2715 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
2715 formatted, pruned = minirst.format(doc, textwidth, keep=keep)
2716 ui.write("\n%s\n" % formatted)
2716 ui.write("\n%s\n" % formatted)
2717 if pruned:
2717 if pruned:
2718 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
2718 ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
2719
2719
2720 if not ui.quiet:
2720 if not ui.quiet:
2721 # options
2721 # options
2722 if entry[1]:
2722 if entry[1]:
2723 optlist.append((_("options:\n"), entry[1]))
2723 optlist.append((_("options:\n"), entry[1]))
2724
2724
2725 addglobalopts(False)
2725 addglobalopts(False)
2726
2726
2727 # check if this command shadows a non-trivial (multi-line)
2727 # check if this command shadows a non-trivial (multi-line)
2728 # extension help text
2728 # extension help text
2729 try:
2729 try:
2730 mod = extensions.find(name)
2730 mod = extensions.find(name)
2731 doc = gettext(mod.__doc__) or ''
2731 doc = gettext(mod.__doc__) or ''
2732 if '\n' in doc.strip():
2732 if '\n' in doc.strip():
2733 msg = _('use "hg help -e %s" to show help for '
2733 msg = _('use "hg help -e %s" to show help for '
2734 'the %s extension') % (name, name)
2734 'the %s extension') % (name, name)
2735 ui.write('\n%s\n' % msg)
2735 ui.write('\n%s\n' % msg)
2736 except KeyError:
2736 except KeyError:
2737 pass
2737 pass
2738
2738
2739 def helplist(header, select=None):
2739 def helplist(header, select=None):
2740 h = {}
2740 h = {}
2741 cmds = {}
2741 cmds = {}
2742 for c, e in table.iteritems():
2742 for c, e in table.iteritems():
2743 f = c.split("|", 1)[0]
2743 f = c.split("|", 1)[0]
2744 if select and not select(f):
2744 if select and not select(f):
2745 continue
2745 continue
2746 if (not select and name != 'shortlist' and
2746 if (not select and name != 'shortlist' and
2747 e[0].__module__ != __name__):
2747 e[0].__module__ != __name__):
2748 continue
2748 continue
2749 if name == "shortlist" and not f.startswith("^"):
2749 if name == "shortlist" and not f.startswith("^"):
2750 continue
2750 continue
2751 f = f.lstrip("^")
2751 f = f.lstrip("^")
2752 if not ui.debugflag and f.startswith("debug"):
2752 if not ui.debugflag and f.startswith("debug"):
2753 continue
2753 continue
2754 doc = e[0].__doc__
2754 doc = e[0].__doc__
2755 if doc and 'DEPRECATED' in doc and not ui.verbose:
2755 if doc and 'DEPRECATED' in doc and not ui.verbose:
2756 continue
2756 continue
2757 doc = gettext(doc)
2757 doc = gettext(doc)
2758 if not doc:
2758 if not doc:
2759 doc = _("(no help text available)")
2759 doc = _("(no help text available)")
2760 h[f] = doc.splitlines()[0].rstrip()
2760 h[f] = doc.splitlines()[0].rstrip()
2761 cmds[f] = c.lstrip("^")
2761 cmds[f] = c.lstrip("^")
2762
2762
2763 if not h:
2763 if not h:
2764 ui.status(_('no commands defined\n'))
2764 ui.status(_('no commands defined\n'))
2765 return
2765 return
2766
2766
2767 ui.status(header)
2767 ui.status(header)
2768 fns = sorted(h)
2768 fns = sorted(h)
2769 m = max(map(len, fns))
2769 m = max(map(len, fns))
2770 for f in fns:
2770 for f in fns:
2771 if ui.verbose:
2771 if ui.verbose:
2772 commands = cmds[f].replace("|",", ")
2772 commands = cmds[f].replace("|",", ")
2773 ui.write(" %s:\n %s\n"%(commands, h[f]))
2773 ui.write(" %s:\n %s\n"%(commands, h[f]))
2774 else:
2774 else:
2775 ui.write('%s\n' % (util.wrap(h[f], textwidth,
2775 ui.write('%s\n' % (util.wrap(h[f], textwidth,
2776 initindent=' %-*s ' % (m, f),
2776 initindent=' %-*s ' % (m, f),
2777 hangindent=' ' * (m + 4))))
2777 hangindent=' ' * (m + 4))))
2778
2778
2779 if not ui.quiet:
2779 if not ui.quiet:
2780 addglobalopts(True)
2780 addglobalopts(True)
2781
2781
2782 def helptopic(name):
2782 def helptopic(name):
2783 for names, header, doc in help.helptable:
2783 for names, header, doc in help.helptable:
2784 if name in names:
2784 if name in names:
2785 break
2785 break
2786 else:
2786 else:
2787 raise error.UnknownCommand(name)
2787 raise error.UnknownCommand(name)
2788
2788
2789 # description
2789 # description
2790 if not doc:
2790 if not doc:
2791 doc = _("(no help text available)")
2791 doc = _("(no help text available)")
2792 if util.safehasattr(doc, '__call__'):
2792 if util.safehasattr(doc, '__call__'):
2793 doc = doc()
2793 doc = doc()
2794
2794
2795 ui.write("%s\n\n" % header)
2795 ui.write("%s\n\n" % header)
2796 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2796 ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
2797 try:
2797 try:
2798 cmdutil.findcmd(name, table)
2798 cmdutil.findcmd(name, table)
2799 ui.write(_('\nuse "hg help -c %s" to see help for '
2799 ui.write(_('\nuse "hg help -c %s" to see help for '
2800 'the %s command\n') % (name, name))
2800 'the %s command\n') % (name, name))
2801 except error.UnknownCommand:
2801 except error.UnknownCommand:
2802 pass
2802 pass
2803
2803
2804 def helpext(name):
2804 def helpext(name):
2805 try:
2805 try:
2806 mod = extensions.find(name)
2806 mod = extensions.find(name)
2807 doc = gettext(mod.__doc__) or _('no help text available')
2807 doc = gettext(mod.__doc__) or _('no help text available')
2808 except KeyError:
2808 except KeyError:
2809 mod = None
2809 mod = None
2810 doc = extensions.disabledext(name)
2810 doc = extensions.disabledext(name)
2811 if not doc:
2811 if not doc:
2812 raise error.UnknownCommand(name)
2812 raise error.UnknownCommand(name)
2813
2813
2814 if '\n' not in doc:
2814 if '\n' not in doc:
2815 head, tail = doc, ""
2815 head, tail = doc, ""
2816 else:
2816 else:
2817 head, tail = doc.split('\n', 1)
2817 head, tail = doc.split('\n', 1)
2818 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2818 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
2819 if tail:
2819 if tail:
2820 ui.write(minirst.format(tail, textwidth))
2820 ui.write(minirst.format(tail, textwidth))
2821 ui.status('\n\n')
2821 ui.status('\n\n')
2822
2822
2823 if mod:
2823 if mod:
2824 try:
2824 try:
2825 ct = mod.cmdtable
2825 ct = mod.cmdtable
2826 except AttributeError:
2826 except AttributeError:
2827 ct = {}
2827 ct = {}
2828 modcmds = set([c.split('|', 1)[0] for c in ct])
2828 modcmds = set([c.split('|', 1)[0] for c in ct])
2829 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2829 helplist(_('list of commands:\n\n'), modcmds.__contains__)
2830 else:
2830 else:
2831 ui.write(_('use "hg help extensions" for information on enabling '
2831 ui.write(_('use "hg help extensions" for information on enabling '
2832 'extensions\n'))
2832 'extensions\n'))
2833
2833
2834 def helpextcmd(name):
2834 def helpextcmd(name):
2835 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2835 cmd, ext, mod = extensions.disabledcmd(ui, name, ui.config('ui', 'strict'))
2836 doc = gettext(mod.__doc__).splitlines()[0]
2836 doc = gettext(mod.__doc__).splitlines()[0]
2837
2837
2838 msg = help.listexts(_("'%s' is provided by the following "
2838 msg = help.listexts(_("'%s' is provided by the following "
2839 "extension:") % cmd, {ext: doc}, indent=4)
2839 "extension:") % cmd, {ext: doc}, indent=4)
2840 ui.write(minirst.format(msg, textwidth))
2840 ui.write(minirst.format(msg, textwidth))
2841 ui.write('\n\n')
2841 ui.write('\n\n')
2842 ui.write(_('use "hg help extensions" for information on enabling '
2842 ui.write(_('use "hg help extensions" for information on enabling '
2843 'extensions\n'))
2843 'extensions\n'))
2844
2844
2845 if name and name != 'shortlist':
2845 if name and name != 'shortlist':
2846 i = None
2846 i = None
2847 if unknowncmd:
2847 if unknowncmd:
2848 queries = (helpextcmd,)
2848 queries = (helpextcmd,)
2849 elif opts.get('extension'):
2849 elif opts.get('extension'):
2850 queries = (helpext,)
2850 queries = (helpext,)
2851 elif opts.get('command'):
2851 elif opts.get('command'):
2852 queries = (helpcmd,)
2852 queries = (helpcmd,)
2853 else:
2853 else:
2854 queries = (helptopic, helpcmd, helpext, helpextcmd)
2854 queries = (helptopic, helpcmd, helpext, helpextcmd)
2855 for f in queries:
2855 for f in queries:
2856 try:
2856 try:
2857 f(name)
2857 f(name)
2858 i = None
2858 i = None
2859 break
2859 break
2860 except error.UnknownCommand, inst:
2860 except error.UnknownCommand, inst:
2861 i = inst
2861 i = inst
2862 if i:
2862 if i:
2863 raise i
2863 raise i
2864
2864
2865 else:
2865 else:
2866 # program name
2866 # program name
2867 ui.status(_("Mercurial Distributed SCM\n"))
2867 ui.status(_("Mercurial Distributed SCM\n"))
2868 ui.status('\n')
2868 ui.status('\n')
2869
2869
2870 # list of commands
2870 # list of commands
2871 if name == "shortlist":
2871 if name == "shortlist":
2872 header = _('basic commands:\n\n')
2872 header = _('basic commands:\n\n')
2873 else:
2873 else:
2874 header = _('list of commands:\n\n')
2874 header = _('list of commands:\n\n')
2875
2875
2876 helplist(header)
2876 helplist(header)
2877 if name != 'shortlist':
2877 if name != 'shortlist':
2878 text = help.listexts(_('enabled extensions:'), extensions.enabled())
2878 text = help.listexts(_('enabled extensions:'), extensions.enabled())
2879 if text:
2879 if text:
2880 ui.write("\n%s\n" % minirst.format(text, textwidth))
2880 ui.write("\n%s\n" % minirst.format(text, textwidth))
2881
2881
2882 if not name:
2882 if not name:
2883 ui.write(_("\nadditional help topics:\n\n"))
2883 ui.write(_("\nadditional help topics:\n\n"))
2884 topics = []
2884 topics = []
2885 for names, header, doc in help.helptable:
2885 for names, header, doc in help.helptable:
2886 topics.append((sorted(names, key=len, reverse=True)[0], header))
2886 topics.append((sorted(names, key=len, reverse=True)[0], header))
2887 topics_len = max([len(s[0]) for s in topics])
2887 topics_len = max([len(s[0]) for s in topics])
2888 for t, desc in topics:
2888 for t, desc in topics:
2889 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2889 ui.write(" %-*s %s\n" % (topics_len, t, desc))
2890
2890
2891 ui.write(opttext(optlist, textwidth))
2891 ui.write(opttext(optlist, textwidth))
2892
2892
2893 @command('identify|id',
2893 @command('identify|id',
2894 [('r', 'rev', '',
2894 [('r', 'rev', '',
2895 _('identify the specified revision'), _('REV')),
2895 _('identify the specified revision'), _('REV')),
2896 ('n', 'num', None, _('show local revision number')),
2896 ('n', 'num', None, _('show local revision number')),
2897 ('i', 'id', None, _('show global revision id')),
2897 ('i', 'id', None, _('show global revision id')),
2898 ('b', 'branch', None, _('show branch')),
2898 ('b', 'branch', None, _('show branch')),
2899 ('t', 'tags', None, _('show tags')),
2899 ('t', 'tags', None, _('show tags')),
2900 ('B', 'bookmarks', None, _('show bookmarks'))],
2900 ('B', 'bookmarks', None, _('show bookmarks'))],
2901 _('[-nibtB] [-r REV] [SOURCE]'))
2901 _('[-nibtB] [-r REV] [SOURCE]'))
2902 def identify(ui, repo, source=None, rev=None,
2902 def identify(ui, repo, source=None, rev=None,
2903 num=None, id=None, branch=None, tags=None, bookmarks=None):
2903 num=None, id=None, branch=None, tags=None, bookmarks=None):
2904 """identify the working copy or specified revision
2904 """identify the working copy or specified revision
2905
2905
2906 Print a summary identifying the repository state at REV using one or
2906 Print a summary identifying the repository state at REV using one or
2907 two parent hash identifiers, followed by a "+" if the working
2907 two parent hash identifiers, followed by a "+" if the working
2908 directory has uncommitted changes, the branch name (if not default),
2908 directory has uncommitted changes, the branch name (if not default),
2909 a list of tags, and a list of bookmarks.
2909 a list of tags, and a list of bookmarks.
2910
2910
2911 When REV is not given, print a summary of the current state of the
2911 When REV is not given, print a summary of the current state of the
2912 repository.
2912 repository.
2913
2913
2914 Specifying a path to a repository root or Mercurial bundle will
2914 Specifying a path to a repository root or Mercurial bundle will
2915 cause lookup to operate on that repository/bundle.
2915 cause lookup to operate on that repository/bundle.
2916
2916
2917 Returns 0 if successful.
2917 Returns 0 if successful.
2918 """
2918 """
2919
2919
2920 if not repo and not source:
2920 if not repo and not source:
2921 raise util.Abort(_("there is no Mercurial repository here "
2921 raise util.Abort(_("there is no Mercurial repository here "
2922 "(.hg not found)"))
2922 "(.hg not found)"))
2923
2923
2924 hexfunc = ui.debugflag and hex or short
2924 hexfunc = ui.debugflag and hex or short
2925 default = not (num or id or branch or tags or bookmarks)
2925 default = not (num or id or branch or tags or bookmarks)
2926 output = []
2926 output = []
2927 revs = []
2927 revs = []
2928
2928
2929 if source:
2929 if source:
2930 source, branches = hg.parseurl(ui.expandpath(source))
2930 source, branches = hg.parseurl(ui.expandpath(source))
2931 repo = hg.peer(ui, {}, source)
2931 repo = hg.peer(ui, {}, source)
2932 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2932 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
2933
2933
2934 if not repo.local():
2934 if not repo.local():
2935 if num or branch or tags:
2935 if num or branch or tags:
2936 raise util.Abort(
2936 raise util.Abort(
2937 _("can't query remote revision number, branch, or tags"))
2937 _("can't query remote revision number, branch, or tags"))
2938 if not rev and revs:
2938 if not rev and revs:
2939 rev = revs[0]
2939 rev = revs[0]
2940 if not rev:
2940 if not rev:
2941 rev = "tip"
2941 rev = "tip"
2942
2942
2943 remoterev = repo.lookup(rev)
2943 remoterev = repo.lookup(rev)
2944 if default or id:
2944 if default or id:
2945 output = [hexfunc(remoterev)]
2945 output = [hexfunc(remoterev)]
2946
2946
2947 def getbms():
2947 def getbms():
2948 bms = []
2948 bms = []
2949
2949
2950 if 'bookmarks' in repo.listkeys('namespaces'):
2950 if 'bookmarks' in repo.listkeys('namespaces'):
2951 hexremoterev = hex(remoterev)
2951 hexremoterev = hex(remoterev)
2952 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
2952 bms = [bm for bm, bmr in repo.listkeys('bookmarks').iteritems()
2953 if bmr == hexremoterev]
2953 if bmr == hexremoterev]
2954
2954
2955 return bms
2955 return bms
2956
2956
2957 if bookmarks:
2957 if bookmarks:
2958 output.extend(getbms())
2958 output.extend(getbms())
2959 elif default and not ui.quiet:
2959 elif default and not ui.quiet:
2960 # multiple bookmarks for a single parent separated by '/'
2960 # multiple bookmarks for a single parent separated by '/'
2961 bm = '/'.join(getbms())
2961 bm = '/'.join(getbms())
2962 if bm:
2962 if bm:
2963 output.append(bm)
2963 output.append(bm)
2964 else:
2964 else:
2965 if not rev:
2965 if not rev:
2966 ctx = repo[None]
2966 ctx = repo[None]
2967 parents = ctx.parents()
2967 parents = ctx.parents()
2968 changed = ""
2968 changed = ""
2969 if default or id or num:
2969 if default or id or num:
2970 changed = util.any(repo.status()) and "+" or ""
2970 changed = util.any(repo.status()) and "+" or ""
2971 if default or id:
2971 if default or id:
2972 output = ["%s%s" %
2972 output = ["%s%s" %
2973 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2973 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
2974 if num:
2974 if num:
2975 output.append("%s%s" %
2975 output.append("%s%s" %
2976 ('+'.join([str(p.rev()) for p in parents]), changed))
2976 ('+'.join([str(p.rev()) for p in parents]), changed))
2977 else:
2977 else:
2978 ctx = scmutil.revsingle(repo, rev)
2978 ctx = scmutil.revsingle(repo, rev)
2979 if default or id:
2979 if default or id:
2980 output = [hexfunc(ctx.node())]
2980 output = [hexfunc(ctx.node())]
2981 if num:
2981 if num:
2982 output.append(str(ctx.rev()))
2982 output.append(str(ctx.rev()))
2983
2983
2984 if default and not ui.quiet:
2984 if default and not ui.quiet:
2985 b = ctx.branch()
2985 b = ctx.branch()
2986 if b != 'default':
2986 if b != 'default':
2987 output.append("(%s)" % b)
2987 output.append("(%s)" % b)
2988
2988
2989 # multiple tags for a single parent separated by '/'
2989 # multiple tags for a single parent separated by '/'
2990 t = '/'.join(ctx.tags())
2990 t = '/'.join(ctx.tags())
2991 if t:
2991 if t:
2992 output.append(t)
2992 output.append(t)
2993
2993
2994 # multiple bookmarks for a single parent separated by '/'
2994 # multiple bookmarks for a single parent separated by '/'
2995 bm = '/'.join(ctx.bookmarks())
2995 bm = '/'.join(ctx.bookmarks())
2996 if bm:
2996 if bm:
2997 output.append(bm)
2997 output.append(bm)
2998 else:
2998 else:
2999 if branch:
2999 if branch:
3000 output.append(ctx.branch())
3000 output.append(ctx.branch())
3001
3001
3002 if tags:
3002 if tags:
3003 output.extend(ctx.tags())
3003 output.extend(ctx.tags())
3004
3004
3005 if bookmarks:
3005 if bookmarks:
3006 output.extend(ctx.bookmarks())
3006 output.extend(ctx.bookmarks())
3007
3007
3008 ui.write("%s\n" % ' '.join(output))
3008 ui.write("%s\n" % ' '.join(output))
3009
3009
3010 @command('import|patch',
3010 @command('import|patch',
3011 [('p', 'strip', 1,
3011 [('p', 'strip', 1,
3012 _('directory strip option for patch. This has the same '
3012 _('directory strip option for patch. This has the same '
3013 'meaning as the corresponding patch option'), _('NUM')),
3013 'meaning as the corresponding patch option'), _('NUM')),
3014 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3014 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3015 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3015 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
3016 ('', 'no-commit', None,
3016 ('', 'no-commit', None,
3017 _("don't commit, just update the working directory")),
3017 _("don't commit, just update the working directory")),
3018 ('', 'bypass', None,
3018 ('', 'bypass', None,
3019 _("apply patch without touching the working directory")),
3019 _("apply patch without touching the working directory")),
3020 ('', 'exact', None,
3020 ('', 'exact', None,
3021 _('apply patch to the nodes from which it was generated')),
3021 _('apply patch to the nodes from which it was generated')),
3022 ('', 'import-branch', None,
3022 ('', 'import-branch', None,
3023 _('use any branch information in patch (implied by --exact)'))] +
3023 _('use any branch information in patch (implied by --exact)'))] +
3024 commitopts + commitopts2 + similarityopts,
3024 commitopts + commitopts2 + similarityopts,
3025 _('[OPTION]... PATCH...'))
3025 _('[OPTION]... PATCH...'))
3026 def import_(ui, repo, patch1, *patches, **opts):
3026 def import_(ui, repo, patch1, *patches, **opts):
3027 """import an ordered set of patches
3027 """import an ordered set of patches
3028
3028
3029 Import a list of patches and commit them individually (unless
3029 Import a list of patches and commit them individually (unless
3030 --no-commit is specified).
3030 --no-commit is specified).
3031
3031
3032 If there are outstanding changes in the working directory, import
3032 If there are outstanding changes in the working directory, import
3033 will abort unless given the -f/--force flag.
3033 will abort unless given the -f/--force flag.
3034
3034
3035 You can import a patch straight from a mail message. Even patches
3035 You can import a patch straight from a mail message. Even patches
3036 as attachments work (to use the body part, it must have type
3036 as attachments work (to use the body part, it must have type
3037 text/plain or text/x-patch). From and Subject headers of email
3037 text/plain or text/x-patch). From and Subject headers of email
3038 message are used as default committer and commit message. All
3038 message are used as default committer and commit message. All
3039 text/plain body parts before first diff are added to commit
3039 text/plain body parts before first diff are added to commit
3040 message.
3040 message.
3041
3041
3042 If the imported patch was generated by :hg:`export`, user and
3042 If the imported patch was generated by :hg:`export`, user and
3043 description from patch override values from message headers and
3043 description from patch override values from message headers and
3044 body. Values given on command line with -m/--message and -u/--user
3044 body. Values given on command line with -m/--message and -u/--user
3045 override these.
3045 override these.
3046
3046
3047 If --exact is specified, import will set the working directory to
3047 If --exact is specified, import will set the working directory to
3048 the parent of each patch before applying it, and will abort if the
3048 the parent of each patch before applying it, and will abort if the
3049 resulting changeset has a different ID than the one recorded in
3049 resulting changeset has a different ID than the one recorded in
3050 the patch. This may happen due to character set problems or other
3050 the patch. This may happen due to character set problems or other
3051 deficiencies in the text patch format.
3051 deficiencies in the text patch format.
3052
3052
3053 Use --bypass to apply and commit patches directly to the
3053 Use --bypass to apply and commit patches directly to the
3054 repository, not touching the working directory. Without --exact,
3054 repository, not touching the working directory. Without --exact,
3055 patches will be applied on top of the working directory parent
3055 patches will be applied on top of the working directory parent
3056 revision.
3056 revision.
3057
3057
3058 With -s/--similarity, hg will attempt to discover renames and
3058 With -s/--similarity, hg will attempt to discover renames and
3059 copies in the patch in the same way as 'addremove'.
3059 copies in the patch in the same way as 'addremove'.
3060
3060
3061 To read a patch from standard input, use "-" as the patch name. If
3061 To read a patch from standard input, use "-" as the patch name. If
3062 a URL is specified, the patch will be downloaded from it.
3062 a URL is specified, the patch will be downloaded from it.
3063 See :hg:`help dates` for a list of formats valid for -d/--date.
3063 See :hg:`help dates` for a list of formats valid for -d/--date.
3064
3064
3065 Returns 0 on success.
3065 Returns 0 on success.
3066 """
3066 """
3067 patches = (patch1,) + patches
3067 patches = (patch1,) + patches
3068
3068
3069 date = opts.get('date')
3069 date = opts.get('date')
3070 if date:
3070 if date:
3071 opts['date'] = util.parsedate(date)
3071 opts['date'] = util.parsedate(date)
3072
3072
3073 update = not opts.get('bypass')
3073 update = not opts.get('bypass')
3074 if not update and opts.get('no_commit'):
3074 if not update and opts.get('no_commit'):
3075 raise util.Abort(_('cannot use --no-commit with --bypass'))
3075 raise util.Abort(_('cannot use --no-commit with --bypass'))
3076 try:
3076 try:
3077 sim = float(opts.get('similarity') or 0)
3077 sim = float(opts.get('similarity') or 0)
3078 except ValueError:
3078 except ValueError:
3079 raise util.Abort(_('similarity must be a number'))
3079 raise util.Abort(_('similarity must be a number'))
3080 if sim < 0 or sim > 100:
3080 if sim < 0 or sim > 100:
3081 raise util.Abort(_('similarity must be between 0 and 100'))
3081 raise util.Abort(_('similarity must be between 0 and 100'))
3082 if sim and not update:
3082 if sim and not update:
3083 raise util.Abort(_('cannot use --similarity with --bypass'))
3083 raise util.Abort(_('cannot use --similarity with --bypass'))
3084
3084
3085 if (opts.get('exact') or not opts.get('force')) and update:
3085 if (opts.get('exact') or not opts.get('force')) and update:
3086 cmdutil.bailifchanged(repo)
3086 cmdutil.bailifchanged(repo)
3087
3087
3088 d = opts["base"]
3088 d = opts["base"]
3089 strip = opts["strip"]
3089 strip = opts["strip"]
3090 wlock = lock = None
3090 wlock = lock = None
3091 msgs = []
3091 msgs = []
3092
3092
3093 def checkexact(repo, n, nodeid):
3093 def checkexact(repo, n, nodeid):
3094 if opts.get('exact') and hex(n) != nodeid:
3094 if opts.get('exact') and hex(n) != nodeid:
3095 repo.rollback()
3095 repo.rollback()
3096 raise util.Abort(_('patch is damaged or loses information'))
3096 raise util.Abort(_('patch is damaged or loses information'))
3097
3097
3098 def tryone(ui, hunk, parents):
3098 def tryone(ui, hunk, parents):
3099 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3099 tmpname, message, user, date, branch, nodeid, p1, p2 = \
3100 patch.extract(ui, hunk)
3100 patch.extract(ui, hunk)
3101
3101
3102 if not tmpname:
3102 if not tmpname:
3103 return None
3103 return None
3104 commitid = _('to working directory')
3104 commitid = _('to working directory')
3105
3105
3106 try:
3106 try:
3107 cmdline_message = cmdutil.logmessage(ui, opts)
3107 cmdline_message = cmdutil.logmessage(ui, opts)
3108 if cmdline_message:
3108 if cmdline_message:
3109 # pickup the cmdline msg
3109 # pickup the cmdline msg
3110 message = cmdline_message
3110 message = cmdline_message
3111 elif message:
3111 elif message:
3112 # pickup the patch msg
3112 # pickup the patch msg
3113 message = message.strip()
3113 message = message.strip()
3114 else:
3114 else:
3115 # launch the editor
3115 # launch the editor
3116 message = None
3116 message = None
3117 ui.debug('message:\n%s\n' % message)
3117 ui.debug('message:\n%s\n' % message)
3118
3118
3119 if len(parents) == 1:
3119 if len(parents) == 1:
3120 parents.append(repo[nullid])
3120 parents.append(repo[nullid])
3121 if opts.get('exact'):
3121 if opts.get('exact'):
3122 if not nodeid or not p1:
3122 if not nodeid or not p1:
3123 raise util.Abort(_('not a Mercurial patch'))
3123 raise util.Abort(_('not a Mercurial patch'))
3124 p1 = repo[p1]
3124 p1 = repo[p1]
3125 p2 = repo[p2 or nullid]
3125 p2 = repo[p2 or nullid]
3126 elif p2:
3126 elif p2:
3127 try:
3127 try:
3128 p1 = repo[p1]
3128 p1 = repo[p1]
3129 p2 = repo[p2]
3129 p2 = repo[p2]
3130 except error.RepoError:
3130 except error.RepoError:
3131 p1, p2 = parents
3131 p1, p2 = parents
3132 else:
3132 else:
3133 p1, p2 = parents
3133 p1, p2 = parents
3134
3134
3135 n = None
3135 n = None
3136 if update:
3136 if update:
3137 if opts.get('exact') and p1 != parents[0]:
3137 if opts.get('exact') and p1 != parents[0]:
3138 hg.clean(repo, p1.node())
3138 hg.clean(repo, p1.node())
3139 if p1 != parents[0] and p2 != parents[1]:
3139 if p1 != parents[0] and p2 != parents[1]:
3140 repo.dirstate.setparents(p1.node(), p2.node())
3140 repo.dirstate.setparents(p1.node(), p2.node())
3141
3141
3142 if opts.get('exact') or opts.get('import_branch'):
3142 if opts.get('exact') or opts.get('import_branch'):
3143 repo.dirstate.setbranch(branch or 'default')
3143 repo.dirstate.setbranch(branch or 'default')
3144
3144
3145 files = set()
3145 files = set()
3146 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3146 patch.patch(ui, repo, tmpname, strip=strip, files=files,
3147 eolmode=None, similarity=sim / 100.0)
3147 eolmode=None, similarity=sim / 100.0)
3148 files = list(files)
3148 files = list(files)
3149 if opts.get('no_commit'):
3149 if opts.get('no_commit'):
3150 if message:
3150 if message:
3151 msgs.append(message)
3151 msgs.append(message)
3152 else:
3152 else:
3153 if opts.get('exact'):
3153 if opts.get('exact'):
3154 m = None
3154 m = None
3155 else:
3155 else:
3156 m = scmutil.matchfiles(repo, files or [])
3156 m = scmutil.matchfiles(repo, files or [])
3157 n = repo.commit(message, opts.get('user') or user,
3157 n = repo.commit(message, opts.get('user') or user,
3158 opts.get('date') or date, match=m,
3158 opts.get('date') or date, match=m,
3159 editor=cmdutil.commiteditor)
3159 editor=cmdutil.commiteditor)
3160 checkexact(repo, n, nodeid)
3160 checkexact(repo, n, nodeid)
3161 # Force a dirstate write so that the next transaction
3161 # Force a dirstate write so that the next transaction
3162 # backups an up-to-date file.
3162 # backups an up-to-date file.
3163 repo.dirstate.write()
3163 repo.dirstate.write()
3164 else:
3164 else:
3165 if opts.get('exact') or opts.get('import_branch'):
3165 if opts.get('exact') or opts.get('import_branch'):
3166 branch = branch or 'default'
3166 branch = branch or 'default'
3167 else:
3167 else:
3168 branch = p1.branch()
3168 branch = p1.branch()
3169 store = patch.filestore()
3169 store = patch.filestore()
3170 try:
3170 try:
3171 files = set()
3171 files = set()
3172 try:
3172 try:
3173 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3173 patch.patchrepo(ui, repo, p1, store, tmpname, strip,
3174 files, eolmode=None)
3174 files, eolmode=None)
3175 except patch.PatchError, e:
3175 except patch.PatchError, e:
3176 raise util.Abort(str(e))
3176 raise util.Abort(str(e))
3177 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3177 memctx = patch.makememctx(repo, (p1.node(), p2.node()),
3178 message,
3178 message,
3179 opts.get('user') or user,
3179 opts.get('user') or user,
3180 opts.get('date') or date,
3180 opts.get('date') or date,
3181 branch, files, store,
3181 branch, files, store,
3182 editor=cmdutil.commiteditor)
3182 editor=cmdutil.commiteditor)
3183 repo.savecommitmessage(memctx.description())
3183 repo.savecommitmessage(memctx.description())
3184 n = memctx.commit()
3184 n = memctx.commit()
3185 checkexact(repo, n, nodeid)
3185 checkexact(repo, n, nodeid)
3186 finally:
3186 finally:
3187 store.close()
3187 store.close()
3188 if n:
3188 if n:
3189 commitid = short(n)
3189 commitid = short(n)
3190 return commitid
3190 return commitid
3191 finally:
3191 finally:
3192 os.unlink(tmpname)
3192 os.unlink(tmpname)
3193
3193
3194 try:
3194 try:
3195 wlock = repo.wlock()
3195 wlock = repo.wlock()
3196 lock = repo.lock()
3196 lock = repo.lock()
3197 parents = repo.parents()
3197 parents = repo.parents()
3198 lastcommit = None
3198 lastcommit = None
3199 for p in patches:
3199 for p in patches:
3200 pf = os.path.join(d, p)
3200 pf = os.path.join(d, p)
3201
3201
3202 if pf == '-':
3202 if pf == '-':
3203 ui.status(_("applying patch from stdin\n"))
3203 ui.status(_("applying patch from stdin\n"))
3204 pf = ui.fin
3204 pf = ui.fin
3205 else:
3205 else:
3206 ui.status(_("applying %s\n") % p)
3206 ui.status(_("applying %s\n") % p)
3207 pf = url.open(ui, pf)
3207 pf = url.open(ui, pf)
3208
3208
3209 haspatch = False
3209 haspatch = False
3210 for hunk in patch.split(pf):
3210 for hunk in patch.split(pf):
3211 commitid = tryone(ui, hunk, parents)
3211 commitid = tryone(ui, hunk, parents)
3212 if commitid:
3212 if commitid:
3213 haspatch = True
3213 haspatch = True
3214 if lastcommit:
3214 if lastcommit:
3215 ui.status(_('applied %s\n') % lastcommit)
3215 ui.status(_('applied %s\n') % lastcommit)
3216 lastcommit = commitid
3216 lastcommit = commitid
3217 if update or opts.get('exact'):
3217 if update or opts.get('exact'):
3218 parents = repo.parents()
3218 parents = repo.parents()
3219 else:
3219 else:
3220 parents = [repo[commitid]]
3220 parents = [repo[commitid]]
3221
3221
3222 if not haspatch:
3222 if not haspatch:
3223 raise util.Abort(_('no diffs found'))
3223 raise util.Abort(_('no diffs found'))
3224
3224
3225 if msgs:
3225 if msgs:
3226 repo.savecommitmessage('\n* * *\n'.join(msgs))
3226 repo.savecommitmessage('\n* * *\n'.join(msgs))
3227 finally:
3227 finally:
3228 release(lock, wlock)
3228 release(lock, wlock)
3229
3229
3230 @command('incoming|in',
3230 @command('incoming|in',
3231 [('f', 'force', None,
3231 [('f', 'force', None,
3232 _('run even if remote repository is unrelated')),
3232 _('run even if remote repository is unrelated')),
3233 ('n', 'newest-first', None, _('show newest record first')),
3233 ('n', 'newest-first', None, _('show newest record first')),
3234 ('', 'bundle', '',
3234 ('', 'bundle', '',
3235 _('file to store the bundles into'), _('FILE')),
3235 _('file to store the bundles into'), _('FILE')),
3236 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3236 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3237 ('B', 'bookmarks', False, _("compare bookmarks")),
3237 ('B', 'bookmarks', False, _("compare bookmarks")),
3238 ('b', 'branch', [],
3238 ('b', 'branch', [],
3239 _('a specific branch you would like to pull'), _('BRANCH')),
3239 _('a specific branch you would like to pull'), _('BRANCH')),
3240 ] + logopts + remoteopts + subrepoopts,
3240 ] + logopts + remoteopts + subrepoopts,
3241 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3241 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
3242 def incoming(ui, repo, source="default", **opts):
3242 def incoming(ui, repo, source="default", **opts):
3243 """show new changesets found in source
3243 """show new changesets found in source
3244
3244
3245 Show new changesets found in the specified path/URL or the default
3245 Show new changesets found in the specified path/URL or the default
3246 pull location. These are the changesets that would have been pulled
3246 pull location. These are the changesets that would have been pulled
3247 if a pull at the time you issued this command.
3247 if a pull at the time you issued this command.
3248
3248
3249 For remote repository, using --bundle avoids downloading the
3249 For remote repository, using --bundle avoids downloading the
3250 changesets twice if the incoming is followed by a pull.
3250 changesets twice if the incoming is followed by a pull.
3251
3251
3252 See pull for valid source format details.
3252 See pull for valid source format details.
3253
3253
3254 Returns 0 if there are incoming changes, 1 otherwise.
3254 Returns 0 if there are incoming changes, 1 otherwise.
3255 """
3255 """
3256 if opts.get('bundle') and opts.get('subrepos'):
3256 if opts.get('bundle') and opts.get('subrepos'):
3257 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3257 raise util.Abort(_('cannot combine --bundle and --subrepos'))
3258
3258
3259 if opts.get('bookmarks'):
3259 if opts.get('bookmarks'):
3260 source, branches = hg.parseurl(ui.expandpath(source),
3260 source, branches = hg.parseurl(ui.expandpath(source),
3261 opts.get('branch'))
3261 opts.get('branch'))
3262 other = hg.peer(repo, opts, source)
3262 other = hg.peer(repo, opts, source)
3263 if 'bookmarks' not in other.listkeys('namespaces'):
3263 if 'bookmarks' not in other.listkeys('namespaces'):
3264 ui.warn(_("remote doesn't support bookmarks\n"))
3264 ui.warn(_("remote doesn't support bookmarks\n"))
3265 return 0
3265 return 0
3266 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3266 ui.status(_('comparing with %s\n') % util.hidepassword(source))
3267 return bookmarks.diff(ui, repo, other)
3267 return bookmarks.diff(ui, repo, other)
3268
3268
3269 repo._subtoppath = ui.expandpath(source)
3269 repo._subtoppath = ui.expandpath(source)
3270 try:
3270 try:
3271 return hg.incoming(ui, repo, source, opts)
3271 return hg.incoming(ui, repo, source, opts)
3272 finally:
3272 finally:
3273 del repo._subtoppath
3273 del repo._subtoppath
3274
3274
3275
3275
3276 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3276 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'))
3277 def init(ui, dest=".", **opts):
3277 def init(ui, dest=".", **opts):
3278 """create a new repository in the given directory
3278 """create a new repository in the given directory
3279
3279
3280 Initialize a new repository in the given directory. If the given
3280 Initialize a new repository in the given directory. If the given
3281 directory does not exist, it will be created.
3281 directory does not exist, it will be created.
3282
3282
3283 If no directory is given, the current directory is used.
3283 If no directory is given, the current directory is used.
3284
3284
3285 It is possible to specify an ``ssh://`` URL as the destination.
3285 It is possible to specify an ``ssh://`` URL as the destination.
3286 See :hg:`help urls` for more information.
3286 See :hg:`help urls` for more information.
3287
3287
3288 Returns 0 on success.
3288 Returns 0 on success.
3289 """
3289 """
3290 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3290 hg.peer(ui, opts, ui.expandpath(dest), create=True)
3291
3291
3292 @command('locate',
3292 @command('locate',
3293 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3293 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3294 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3294 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3295 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3295 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
3296 ] + walkopts,
3296 ] + walkopts,
3297 _('[OPTION]... [PATTERN]...'))
3297 _('[OPTION]... [PATTERN]...'))
3298 def locate(ui, repo, *pats, **opts):
3298 def locate(ui, repo, *pats, **opts):
3299 """locate files matching specific patterns
3299 """locate files matching specific patterns
3300
3300
3301 Print files under Mercurial control in the working directory whose
3301 Print files under Mercurial control in the working directory whose
3302 names match the given patterns.
3302 names match the given patterns.
3303
3303
3304 By default, this command searches all directories in the working
3304 By default, this command searches all directories in the working
3305 directory. To search just the current directory and its
3305 directory. To search just the current directory and its
3306 subdirectories, use "--include .".
3306 subdirectories, use "--include .".
3307
3307
3308 If no patterns are given to match, this command prints the names
3308 If no patterns are given to match, this command prints the names
3309 of all files under Mercurial control in the working directory.
3309 of all files under Mercurial control in the working directory.
3310
3310
3311 If you want to feed the output of this command into the "xargs"
3311 If you want to feed the output of this command into the "xargs"
3312 command, use the -0 option to both this command and "xargs". This
3312 command, use the -0 option to both this command and "xargs". This
3313 will avoid the problem of "xargs" treating single filenames that
3313 will avoid the problem of "xargs" treating single filenames that
3314 contain whitespace as multiple filenames.
3314 contain whitespace as multiple filenames.
3315
3315
3316 Returns 0 if a match is found, 1 otherwise.
3316 Returns 0 if a match is found, 1 otherwise.
3317 """
3317 """
3318 end = opts.get('print0') and '\0' or '\n'
3318 end = opts.get('print0') and '\0' or '\n'
3319 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3319 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
3320
3320
3321 ret = 1
3321 ret = 1
3322 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3322 m = scmutil.match(repo[rev], pats, opts, default='relglob')
3323 m.bad = lambda x, y: False
3323 m.bad = lambda x, y: False
3324 for abs in repo[rev].walk(m):
3324 for abs in repo[rev].walk(m):
3325 if not rev and abs not in repo.dirstate:
3325 if not rev and abs not in repo.dirstate:
3326 continue
3326 continue
3327 if opts.get('fullpath'):
3327 if opts.get('fullpath'):
3328 ui.write(repo.wjoin(abs), end)
3328 ui.write(repo.wjoin(abs), end)
3329 else:
3329 else:
3330 ui.write(((pats and m.rel(abs)) or abs), end)
3330 ui.write(((pats and m.rel(abs)) or abs), end)
3331 ret = 0
3331 ret = 0
3332
3332
3333 return ret
3333 return ret
3334
3334
3335 @command('^log|history',
3335 @command('^log|history',
3336 [('f', 'follow', None,
3336 [('f', 'follow', None,
3337 _('follow changeset history, or file history across copies and renames')),
3337 _('follow changeset history, or file history across copies and renames')),
3338 ('', 'follow-first', None,
3338 ('', 'follow-first', None,
3339 _('only follow the first parent of merge changesets')),
3339 _('only follow the first parent of merge changesets')),
3340 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3340 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
3341 ('C', 'copies', None, _('show copied files')),
3341 ('C', 'copies', None, _('show copied files')),
3342 ('k', 'keyword', [],
3342 ('k', 'keyword', [],
3343 _('do case-insensitive search for a given text'), _('TEXT')),
3343 _('do case-insensitive search for a given text'), _('TEXT')),
3344 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3344 ('r', 'rev', [], _('show the specified revision or range'), _('REV')),
3345 ('', 'removed', None, _('include revisions where files were removed')),
3345 ('', 'removed', None, _('include revisions where files were removed')),
3346 ('m', 'only-merges', None, _('show only merges')),
3346 ('m', 'only-merges', None, _('show only merges')),
3347 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3347 ('u', 'user', [], _('revisions committed by user'), _('USER')),
3348 ('', 'only-branch', [],
3348 ('', 'only-branch', [],
3349 _('show only changesets within the given named branch (DEPRECATED)'),
3349 _('show only changesets within the given named branch (DEPRECATED)'),
3350 _('BRANCH')),
3350 _('BRANCH')),
3351 ('b', 'branch', [],
3351 ('b', 'branch', [],
3352 _('show changesets within the given named branch'), _('BRANCH')),
3352 _('show changesets within the given named branch'), _('BRANCH')),
3353 ('P', 'prune', [],
3353 ('P', 'prune', [],
3354 _('do not display revision or any of its ancestors'), _('REV')),
3354 _('do not display revision or any of its ancestors'), _('REV')),
3355 ('h', 'hidden', False, _('show hidden changesets')),
3355 ('', 'hidden', False, _('show hidden changesets')),
3356 ] + logopts + walkopts,
3356 ] + logopts + walkopts,
3357 _('[OPTION]... [FILE]'))
3357 _('[OPTION]... [FILE]'))
3358 def log(ui, repo, *pats, **opts):
3358 def log(ui, repo, *pats, **opts):
3359 """show revision history of entire repository or files
3359 """show revision history of entire repository or files
3360
3360
3361 Print the revision history of the specified files or the entire
3361 Print the revision history of the specified files or the entire
3362 project.
3362 project.
3363
3363
3364 File history is shown without following rename or copy history of
3364 File history is shown without following rename or copy history of
3365 files. Use -f/--follow with a filename to follow history across
3365 files. Use -f/--follow with a filename to follow history across
3366 renames and copies. --follow without a filename will only show
3366 renames and copies. --follow without a filename will only show
3367 ancestors or descendants of the starting revision. --follow-first
3367 ancestors or descendants of the starting revision. --follow-first
3368 only follows the first parent of merge revisions.
3368 only follows the first parent of merge revisions.
3369
3369
3370 If no revision range is specified, the default is ``tip:0`` unless
3370 If no revision range is specified, the default is ``tip:0`` unless
3371 --follow is set, in which case the working directory parent is
3371 --follow is set, in which case the working directory parent is
3372 used as the starting revision. You can specify a revision set for
3372 used as the starting revision. You can specify a revision set for
3373 log, see :hg:`help revsets` for more information.
3373 log, see :hg:`help revsets` for more information.
3374
3374
3375 See :hg:`help dates` for a list of formats valid for -d/--date.
3375 See :hg:`help dates` for a list of formats valid for -d/--date.
3376
3376
3377 By default this command prints revision number and changeset id,
3377 By default this command prints revision number and changeset id,
3378 tags, non-trivial parents, user, date and time, and a summary for
3378 tags, non-trivial parents, user, date and time, and a summary for
3379 each commit. When the -v/--verbose switch is used, the list of
3379 each commit. When the -v/--verbose switch is used, the list of
3380 changed files and full commit message are shown.
3380 changed files and full commit message are shown.
3381
3381
3382 .. note::
3382 .. note::
3383 log -p/--patch may generate unexpected diff output for merge
3383 log -p/--patch may generate unexpected diff output for merge
3384 changesets, as it will only compare the merge changeset against
3384 changesets, as it will only compare the merge changeset against
3385 its first parent. Also, only files different from BOTH parents
3385 its first parent. Also, only files different from BOTH parents
3386 will appear in files:.
3386 will appear in files:.
3387
3387
3388 Returns 0 on success.
3388 Returns 0 on success.
3389 """
3389 """
3390
3390
3391 matchfn = scmutil.match(repo[None], pats, opts)
3391 matchfn = scmutil.match(repo[None], pats, opts)
3392 limit = cmdutil.loglimit(opts)
3392 limit = cmdutil.loglimit(opts)
3393 count = 0
3393 count = 0
3394
3394
3395 endrev = None
3395 endrev = None
3396 if opts.get('copies') and opts.get('rev'):
3396 if opts.get('copies') and opts.get('rev'):
3397 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3397 endrev = max(scmutil.revrange(repo, opts.get('rev'))) + 1
3398
3398
3399 df = False
3399 df = False
3400 if opts["date"]:
3400 if opts["date"]:
3401 df = util.matchdate(opts["date"])
3401 df = util.matchdate(opts["date"])
3402
3402
3403 branches = opts.get('branch', []) + opts.get('only_branch', [])
3403 branches = opts.get('branch', []) + opts.get('only_branch', [])
3404 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3404 opts['branch'] = [repo.lookupbranch(b) for b in branches]
3405
3405
3406 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3406 displayer = cmdutil.show_changeset(ui, repo, opts, True)
3407 def prep(ctx, fns):
3407 def prep(ctx, fns):
3408 rev = ctx.rev()
3408 rev = ctx.rev()
3409 parents = [p for p in repo.changelog.parentrevs(rev)
3409 parents = [p for p in repo.changelog.parentrevs(rev)
3410 if p != nullrev]
3410 if p != nullrev]
3411 if opts.get('no_merges') and len(parents) == 2:
3411 if opts.get('no_merges') and len(parents) == 2:
3412 return
3412 return
3413 if opts.get('only_merges') and len(parents) != 2:
3413 if opts.get('only_merges') and len(parents) != 2:
3414 return
3414 return
3415 if opts.get('branch') and ctx.branch() not in opts['branch']:
3415 if opts.get('branch') and ctx.branch() not in opts['branch']:
3416 return
3416 return
3417 if not opts.get('hidden') and ctx.hidden():
3417 if not opts.get('hidden') and ctx.hidden():
3418 return
3418 return
3419 if df and not df(ctx.date()[0]):
3419 if df and not df(ctx.date()[0]):
3420 return
3420 return
3421 if opts['user'] and not [k for k in opts['user']
3421 if opts['user'] and not [k for k in opts['user']
3422 if k.lower() in ctx.user().lower()]:
3422 if k.lower() in ctx.user().lower()]:
3423 return
3423 return
3424 if opts.get('keyword'):
3424 if opts.get('keyword'):
3425 for k in [kw.lower() for kw in opts['keyword']]:
3425 for k in [kw.lower() for kw in opts['keyword']]:
3426 if (k in ctx.user().lower() or
3426 if (k in ctx.user().lower() or
3427 k in ctx.description().lower() or
3427 k in ctx.description().lower() or
3428 k in " ".join(ctx.files()).lower()):
3428 k in " ".join(ctx.files()).lower()):
3429 break
3429 break
3430 else:
3430 else:
3431 return
3431 return
3432
3432
3433 copies = None
3433 copies = None
3434 if opts.get('copies') and rev:
3434 if opts.get('copies') and rev:
3435 copies = []
3435 copies = []
3436 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3436 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
3437 for fn in ctx.files():
3437 for fn in ctx.files():
3438 rename = getrenamed(fn, rev)
3438 rename = getrenamed(fn, rev)
3439 if rename:
3439 if rename:
3440 copies.append((fn, rename[0]))
3440 copies.append((fn, rename[0]))
3441
3441
3442 revmatchfn = None
3442 revmatchfn = None
3443 if opts.get('patch') or opts.get('stat'):
3443 if opts.get('patch') or opts.get('stat'):
3444 if opts.get('follow') or opts.get('follow_first'):
3444 if opts.get('follow') or opts.get('follow_first'):
3445 # note: this might be wrong when following through merges
3445 # note: this might be wrong when following through merges
3446 revmatchfn = scmutil.match(repo[None], fns, default='path')
3446 revmatchfn = scmutil.match(repo[None], fns, default='path')
3447 else:
3447 else:
3448 revmatchfn = matchfn
3448 revmatchfn = matchfn
3449
3449
3450 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3450 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
3451
3451
3452 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3452 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3453 if count == limit:
3453 if count == limit:
3454 break
3454 break
3455 if displayer.flush(ctx.rev()):
3455 if displayer.flush(ctx.rev()):
3456 count += 1
3456 count += 1
3457 displayer.close()
3457 displayer.close()
3458
3458
3459 @command('manifest',
3459 @command('manifest',
3460 [('r', 'rev', '', _('revision to display'), _('REV')),
3460 [('r', 'rev', '', _('revision to display'), _('REV')),
3461 ('', 'all', False, _("list files from all revisions"))],
3461 ('', 'all', False, _("list files from all revisions"))],
3462 _('[-r REV]'))
3462 _('[-r REV]'))
3463 def manifest(ui, repo, node=None, rev=None, **opts):
3463 def manifest(ui, repo, node=None, rev=None, **opts):
3464 """output the current or given revision of the project manifest
3464 """output the current or given revision of the project manifest
3465
3465
3466 Print a list of version controlled files for the given revision.
3466 Print a list of version controlled files for the given revision.
3467 If no revision is given, the first parent of the working directory
3467 If no revision is given, the first parent of the working directory
3468 is used, or the null revision if no revision is checked out.
3468 is used, or the null revision if no revision is checked out.
3469
3469
3470 With -v, print file permissions, symlink and executable bits.
3470 With -v, print file permissions, symlink and executable bits.
3471 With --debug, print file revision hashes.
3471 With --debug, print file revision hashes.
3472
3472
3473 If option --all is specified, the list of all files from all revisions
3473 If option --all is specified, the list of all files from all revisions
3474 is printed. This includes deleted and renamed files.
3474 is printed. This includes deleted and renamed files.
3475
3475
3476 Returns 0 on success.
3476 Returns 0 on success.
3477 """
3477 """
3478 if opts.get('all'):
3478 if opts.get('all'):
3479 if rev or node:
3479 if rev or node:
3480 raise util.Abort(_("can't specify a revision with --all"))
3480 raise util.Abort(_("can't specify a revision with --all"))
3481
3481
3482 res = []
3482 res = []
3483 prefix = "data/"
3483 prefix = "data/"
3484 suffix = ".i"
3484 suffix = ".i"
3485 plen = len(prefix)
3485 plen = len(prefix)
3486 slen = len(suffix)
3486 slen = len(suffix)
3487 lock = repo.lock()
3487 lock = repo.lock()
3488 try:
3488 try:
3489 for fn, b, size in repo.store.datafiles():
3489 for fn, b, size in repo.store.datafiles():
3490 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3490 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
3491 res.append(fn[plen:-slen])
3491 res.append(fn[plen:-slen])
3492 finally:
3492 finally:
3493 lock.release()
3493 lock.release()
3494 for f in sorted(res):
3494 for f in sorted(res):
3495 ui.write("%s\n" % f)
3495 ui.write("%s\n" % f)
3496 return
3496 return
3497
3497
3498 if rev and node:
3498 if rev and node:
3499 raise util.Abort(_("please specify just one revision"))
3499 raise util.Abort(_("please specify just one revision"))
3500
3500
3501 if not node:
3501 if not node:
3502 node = rev
3502 node = rev
3503
3503
3504 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
3504 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
3505 ctx = scmutil.revsingle(repo, node)
3505 ctx = scmutil.revsingle(repo, node)
3506 for f in ctx:
3506 for f in ctx:
3507 if ui.debugflag:
3507 if ui.debugflag:
3508 ui.write("%40s " % hex(ctx.manifest()[f]))
3508 ui.write("%40s " % hex(ctx.manifest()[f]))
3509 if ui.verbose:
3509 if ui.verbose:
3510 ui.write(decor[ctx.flags(f)])
3510 ui.write(decor[ctx.flags(f)])
3511 ui.write("%s\n" % f)
3511 ui.write("%s\n" % f)
3512
3512
3513 @command('^merge',
3513 @command('^merge',
3514 [('f', 'force', None, _('force a merge with outstanding changes')),
3514 [('f', 'force', None, _('force a merge with outstanding changes')),
3515 ('r', 'rev', '', _('revision to merge'), _('REV')),
3515 ('r', 'rev', '', _('revision to merge'), _('REV')),
3516 ('P', 'preview', None,
3516 ('P', 'preview', None,
3517 _('review revisions to merge (no merge is performed)'))
3517 _('review revisions to merge (no merge is performed)'))
3518 ] + mergetoolopts,
3518 ] + mergetoolopts,
3519 _('[-P] [-f] [[-r] REV]'))
3519 _('[-P] [-f] [[-r] REV]'))
3520 def merge(ui, repo, node=None, **opts):
3520 def merge(ui, repo, node=None, **opts):
3521 """merge working directory with another revision
3521 """merge working directory with another revision
3522
3522
3523 The current working directory is updated with all changes made in
3523 The current working directory is updated with all changes made in
3524 the requested revision since the last common predecessor revision.
3524 the requested revision since the last common predecessor revision.
3525
3525
3526 Files that changed between either parent are marked as changed for
3526 Files that changed between either parent are marked as changed for
3527 the next commit and a commit must be performed before any further
3527 the next commit and a commit must be performed before any further
3528 updates to the repository are allowed. The next commit will have
3528 updates to the repository are allowed. The next commit will have
3529 two parents.
3529 two parents.
3530
3530
3531 ``--tool`` can be used to specify the merge tool used for file
3531 ``--tool`` can be used to specify the merge tool used for file
3532 merges. It overrides the HGMERGE environment variable and your
3532 merges. It overrides the HGMERGE environment variable and your
3533 configuration files. See :hg:`help merge-tools` for options.
3533 configuration files. See :hg:`help merge-tools` for options.
3534
3534
3535 If no revision is specified, the working directory's parent is a
3535 If no revision is specified, the working directory's parent is a
3536 head revision, and the current branch contains exactly one other
3536 head revision, and the current branch contains exactly one other
3537 head, the other head is merged with by default. Otherwise, an
3537 head, the other head is merged with by default. Otherwise, an
3538 explicit revision with which to merge with must be provided.
3538 explicit revision with which to merge with must be provided.
3539
3539
3540 :hg:`resolve` must be used to resolve unresolved files.
3540 :hg:`resolve` must be used to resolve unresolved files.
3541
3541
3542 To undo an uncommitted merge, use :hg:`update --clean .` which
3542 To undo an uncommitted merge, use :hg:`update --clean .` which
3543 will check out a clean copy of the original merge parent, losing
3543 will check out a clean copy of the original merge parent, losing
3544 all changes.
3544 all changes.
3545
3545
3546 Returns 0 on success, 1 if there are unresolved files.
3546 Returns 0 on success, 1 if there are unresolved files.
3547 """
3547 """
3548
3548
3549 if opts.get('rev') and node:
3549 if opts.get('rev') and node:
3550 raise util.Abort(_("please specify just one revision"))
3550 raise util.Abort(_("please specify just one revision"))
3551 if not node:
3551 if not node:
3552 node = opts.get('rev')
3552 node = opts.get('rev')
3553
3553
3554 if not node:
3554 if not node:
3555 branch = repo[None].branch()
3555 branch = repo[None].branch()
3556 bheads = repo.branchheads(branch)
3556 bheads = repo.branchheads(branch)
3557 if len(bheads) > 2:
3557 if len(bheads) > 2:
3558 raise util.Abort(_("branch '%s' has %d heads - "
3558 raise util.Abort(_("branch '%s' has %d heads - "
3559 "please merge with an explicit rev")
3559 "please merge with an explicit rev")
3560 % (branch, len(bheads)),
3560 % (branch, len(bheads)),
3561 hint=_("run 'hg heads .' to see heads"))
3561 hint=_("run 'hg heads .' to see heads"))
3562
3562
3563 parent = repo.dirstate.p1()
3563 parent = repo.dirstate.p1()
3564 if len(bheads) == 1:
3564 if len(bheads) == 1:
3565 if len(repo.heads()) > 1:
3565 if len(repo.heads()) > 1:
3566 raise util.Abort(_("branch '%s' has one head - "
3566 raise util.Abort(_("branch '%s' has one head - "
3567 "please merge with an explicit rev")
3567 "please merge with an explicit rev")
3568 % branch,
3568 % branch,
3569 hint=_("run 'hg heads' to see all heads"))
3569 hint=_("run 'hg heads' to see all heads"))
3570 msg = _('there is nothing to merge')
3570 msg = _('there is nothing to merge')
3571 if parent != repo.lookup(repo[None].branch()):
3571 if parent != repo.lookup(repo[None].branch()):
3572 msg = _('%s - use "hg update" instead') % msg
3572 msg = _('%s - use "hg update" instead') % msg
3573 raise util.Abort(msg)
3573 raise util.Abort(msg)
3574
3574
3575 if parent not in bheads:
3575 if parent not in bheads:
3576 raise util.Abort(_('working directory not at a head revision'),
3576 raise util.Abort(_('working directory not at a head revision'),
3577 hint=_("use 'hg update' or merge with an "
3577 hint=_("use 'hg update' or merge with an "
3578 "explicit revision"))
3578 "explicit revision"))
3579 node = parent == bheads[0] and bheads[-1] or bheads[0]
3579 node = parent == bheads[0] and bheads[-1] or bheads[0]
3580 else:
3580 else:
3581 node = scmutil.revsingle(repo, node).node()
3581 node = scmutil.revsingle(repo, node).node()
3582
3582
3583 if opts.get('preview'):
3583 if opts.get('preview'):
3584 # find nodes that are ancestors of p2 but not of p1
3584 # find nodes that are ancestors of p2 but not of p1
3585 p1 = repo.lookup('.')
3585 p1 = repo.lookup('.')
3586 p2 = repo.lookup(node)
3586 p2 = repo.lookup(node)
3587 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3587 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
3588
3588
3589 displayer = cmdutil.show_changeset(ui, repo, opts)
3589 displayer = cmdutil.show_changeset(ui, repo, opts)
3590 for node in nodes:
3590 for node in nodes:
3591 displayer.show(repo[node])
3591 displayer.show(repo[node])
3592 displayer.close()
3592 displayer.close()
3593 return 0
3593 return 0
3594
3594
3595 try:
3595 try:
3596 # ui.forcemerge is an internal variable, do not document
3596 # ui.forcemerge is an internal variable, do not document
3597 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3597 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
3598 return hg.merge(repo, node, force=opts.get('force'))
3598 return hg.merge(repo, node, force=opts.get('force'))
3599 finally:
3599 finally:
3600 ui.setconfig('ui', 'forcemerge', '')
3600 ui.setconfig('ui', 'forcemerge', '')
3601
3601
3602 @command('outgoing|out',
3602 @command('outgoing|out',
3603 [('f', 'force', None, _('run even when the destination is unrelated')),
3603 [('f', 'force', None, _('run even when the destination is unrelated')),
3604 ('r', 'rev', [],
3604 ('r', 'rev', [],
3605 _('a changeset intended to be included in the destination'), _('REV')),
3605 _('a changeset intended to be included in the destination'), _('REV')),
3606 ('n', 'newest-first', None, _('show newest record first')),
3606 ('n', 'newest-first', None, _('show newest record first')),
3607 ('B', 'bookmarks', False, _('compare bookmarks')),
3607 ('B', 'bookmarks', False, _('compare bookmarks')),
3608 ('b', 'branch', [], _('a specific branch you would like to push'),
3608 ('b', 'branch', [], _('a specific branch you would like to push'),
3609 _('BRANCH')),
3609 _('BRANCH')),
3610 ] + logopts + remoteopts + subrepoopts,
3610 ] + logopts + remoteopts + subrepoopts,
3611 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3611 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
3612 def outgoing(ui, repo, dest=None, **opts):
3612 def outgoing(ui, repo, dest=None, **opts):
3613 """show changesets not found in the destination
3613 """show changesets not found in the destination
3614
3614
3615 Show changesets not found in the specified destination repository
3615 Show changesets not found in the specified destination repository
3616 or the default push location. These are the changesets that would
3616 or the default push location. These are the changesets that would
3617 be pushed if a push was requested.
3617 be pushed if a push was requested.
3618
3618
3619 See pull for details of valid destination formats.
3619 See pull for details of valid destination formats.
3620
3620
3621 Returns 0 if there are outgoing changes, 1 otherwise.
3621 Returns 0 if there are outgoing changes, 1 otherwise.
3622 """
3622 """
3623
3623
3624 if opts.get('bookmarks'):
3624 if opts.get('bookmarks'):
3625 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3625 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3626 dest, branches = hg.parseurl(dest, opts.get('branch'))
3626 dest, branches = hg.parseurl(dest, opts.get('branch'))
3627 other = hg.peer(repo, opts, dest)
3627 other = hg.peer(repo, opts, dest)
3628 if 'bookmarks' not in other.listkeys('namespaces'):
3628 if 'bookmarks' not in other.listkeys('namespaces'):
3629 ui.warn(_("remote doesn't support bookmarks\n"))
3629 ui.warn(_("remote doesn't support bookmarks\n"))
3630 return 0
3630 return 0
3631 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3631 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
3632 return bookmarks.diff(ui, other, repo)
3632 return bookmarks.diff(ui, other, repo)
3633
3633
3634 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3634 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
3635 try:
3635 try:
3636 return hg.outgoing(ui, repo, dest, opts)
3636 return hg.outgoing(ui, repo, dest, opts)
3637 finally:
3637 finally:
3638 del repo._subtoppath
3638 del repo._subtoppath
3639
3639
3640 @command('parents',
3640 @command('parents',
3641 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3641 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
3642 ] + templateopts,
3642 ] + templateopts,
3643 _('[-r REV] [FILE]'))
3643 _('[-r REV] [FILE]'))
3644 def parents(ui, repo, file_=None, **opts):
3644 def parents(ui, repo, file_=None, **opts):
3645 """show the parents of the working directory or revision
3645 """show the parents of the working directory or revision
3646
3646
3647 Print the working directory's parent revisions. If a revision is
3647 Print the working directory's parent revisions. If a revision is
3648 given via -r/--rev, the parent of that revision will be printed.
3648 given via -r/--rev, the parent of that revision will be printed.
3649 If a file argument is given, the revision in which the file was
3649 If a file argument is given, the revision in which the file was
3650 last changed (before the working directory revision or the
3650 last changed (before the working directory revision or the
3651 argument to --rev if given) is printed.
3651 argument to --rev if given) is printed.
3652
3652
3653 Returns 0 on success.
3653 Returns 0 on success.
3654 """
3654 """
3655
3655
3656 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3656 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3657
3657
3658 if file_:
3658 if file_:
3659 m = scmutil.match(ctx, (file_,), opts)
3659 m = scmutil.match(ctx, (file_,), opts)
3660 if m.anypats() or len(m.files()) != 1:
3660 if m.anypats() or len(m.files()) != 1:
3661 raise util.Abort(_('can only specify an explicit filename'))
3661 raise util.Abort(_('can only specify an explicit filename'))
3662 file_ = m.files()[0]
3662 file_ = m.files()[0]
3663 filenodes = []
3663 filenodes = []
3664 for cp in ctx.parents():
3664 for cp in ctx.parents():
3665 if not cp:
3665 if not cp:
3666 continue
3666 continue
3667 try:
3667 try:
3668 filenodes.append(cp.filenode(file_))
3668 filenodes.append(cp.filenode(file_))
3669 except error.LookupError:
3669 except error.LookupError:
3670 pass
3670 pass
3671 if not filenodes:
3671 if not filenodes:
3672 raise util.Abort(_("'%s' not found in manifest!") % file_)
3672 raise util.Abort(_("'%s' not found in manifest!") % file_)
3673 fl = repo.file(file_)
3673 fl = repo.file(file_)
3674 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
3674 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
3675 else:
3675 else:
3676 p = [cp.node() for cp in ctx.parents()]
3676 p = [cp.node() for cp in ctx.parents()]
3677
3677
3678 displayer = cmdutil.show_changeset(ui, repo, opts)
3678 displayer = cmdutil.show_changeset(ui, repo, opts)
3679 for n in p:
3679 for n in p:
3680 if n != nullid:
3680 if n != nullid:
3681 displayer.show(repo[n])
3681 displayer.show(repo[n])
3682 displayer.close()
3682 displayer.close()
3683
3683
3684 @command('paths', [], _('[NAME]'))
3684 @command('paths', [], _('[NAME]'))
3685 def paths(ui, repo, search=None):
3685 def paths(ui, repo, search=None):
3686 """show aliases for remote repositories
3686 """show aliases for remote repositories
3687
3687
3688 Show definition of symbolic path name NAME. If no name is given,
3688 Show definition of symbolic path name NAME. If no name is given,
3689 show definition of all available names.
3689 show definition of all available names.
3690
3690
3691 Option -q/--quiet suppresses all output when searching for NAME
3691 Option -q/--quiet suppresses all output when searching for NAME
3692 and shows only the path names when listing all definitions.
3692 and shows only the path names when listing all definitions.
3693
3693
3694 Path names are defined in the [paths] section of your
3694 Path names are defined in the [paths] section of your
3695 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3695 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
3696 repository, ``.hg/hgrc`` is used, too.
3696 repository, ``.hg/hgrc`` is used, too.
3697
3697
3698 The path names ``default`` and ``default-push`` have a special
3698 The path names ``default`` and ``default-push`` have a special
3699 meaning. When performing a push or pull operation, they are used
3699 meaning. When performing a push or pull operation, they are used
3700 as fallbacks if no location is specified on the command-line.
3700 as fallbacks if no location is specified on the command-line.
3701 When ``default-push`` is set, it will be used for push and
3701 When ``default-push`` is set, it will be used for push and
3702 ``default`` will be used for pull; otherwise ``default`` is used
3702 ``default`` will be used for pull; otherwise ``default`` is used
3703 as the fallback for both. When cloning a repository, the clone
3703 as the fallback for both. When cloning a repository, the clone
3704 source is written as ``default`` in ``.hg/hgrc``. Note that
3704 source is written as ``default`` in ``.hg/hgrc``. Note that
3705 ``default`` and ``default-push`` apply to all inbound (e.g.
3705 ``default`` and ``default-push`` apply to all inbound (e.g.
3706 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
3706 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
3707 :hg:`bundle`) operations.
3707 :hg:`bundle`) operations.
3708
3708
3709 See :hg:`help urls` for more information.
3709 See :hg:`help urls` for more information.
3710
3710
3711 Returns 0 on success.
3711 Returns 0 on success.
3712 """
3712 """
3713 if search:
3713 if search:
3714 for name, path in ui.configitems("paths"):
3714 for name, path in ui.configitems("paths"):
3715 if name == search:
3715 if name == search:
3716 ui.status("%s\n" % util.hidepassword(path))
3716 ui.status("%s\n" % util.hidepassword(path))
3717 return
3717 return
3718 if not ui.quiet:
3718 if not ui.quiet:
3719 ui.warn(_("not found!\n"))
3719 ui.warn(_("not found!\n"))
3720 return 1
3720 return 1
3721 else:
3721 else:
3722 for name, path in ui.configitems("paths"):
3722 for name, path in ui.configitems("paths"):
3723 if ui.quiet:
3723 if ui.quiet:
3724 ui.write("%s\n" % name)
3724 ui.write("%s\n" % name)
3725 else:
3725 else:
3726 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
3726 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
3727
3727
3728 def postincoming(ui, repo, modheads, optupdate, checkout):
3728 def postincoming(ui, repo, modheads, optupdate, checkout):
3729 if modheads == 0:
3729 if modheads == 0:
3730 return
3730 return
3731 if optupdate:
3731 if optupdate:
3732 try:
3732 try:
3733 return hg.update(repo, checkout)
3733 return hg.update(repo, checkout)
3734 except util.Abort, inst:
3734 except util.Abort, inst:
3735 ui.warn(_("not updating: %s\n" % str(inst)))
3735 ui.warn(_("not updating: %s\n" % str(inst)))
3736 return 0
3736 return 0
3737 if modheads > 1:
3737 if modheads > 1:
3738 currentbranchheads = len(repo.branchheads())
3738 currentbranchheads = len(repo.branchheads())
3739 if currentbranchheads == modheads:
3739 if currentbranchheads == modheads:
3740 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3740 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
3741 elif currentbranchheads > 1:
3741 elif currentbranchheads > 1:
3742 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
3742 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to merge)\n"))
3743 else:
3743 else:
3744 ui.status(_("(run 'hg heads' to see heads)\n"))
3744 ui.status(_("(run 'hg heads' to see heads)\n"))
3745 else:
3745 else:
3746 ui.status(_("(run 'hg update' to get a working copy)\n"))
3746 ui.status(_("(run 'hg update' to get a working copy)\n"))
3747
3747
3748 @command('^pull',
3748 @command('^pull',
3749 [('u', 'update', None,
3749 [('u', 'update', None,
3750 _('update to new branch head if changesets were pulled')),
3750 _('update to new branch head if changesets were pulled')),
3751 ('f', 'force', None, _('run even when remote repository is unrelated')),
3751 ('f', 'force', None, _('run even when remote repository is unrelated')),
3752 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3752 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
3753 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3753 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
3754 ('b', 'branch', [], _('a specific branch you would like to pull'),
3754 ('b', 'branch', [], _('a specific branch you would like to pull'),
3755 _('BRANCH')),
3755 _('BRANCH')),
3756 ] + remoteopts,
3756 ] + remoteopts,
3757 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3757 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
3758 def pull(ui, repo, source="default", **opts):
3758 def pull(ui, repo, source="default", **opts):
3759 """pull changes from the specified source
3759 """pull changes from the specified source
3760
3760
3761 Pull changes from a remote repository to a local one.
3761 Pull changes from a remote repository to a local one.
3762
3762
3763 This finds all changes from the repository at the specified path
3763 This finds all changes from the repository at the specified path
3764 or URL and adds them to a local repository (the current one unless
3764 or URL and adds them to a local repository (the current one unless
3765 -R is specified). By default, this does not update the copy of the
3765 -R is specified). By default, this does not update the copy of the
3766 project in the working directory.
3766 project in the working directory.
3767
3767
3768 Use :hg:`incoming` if you want to see what would have been added
3768 Use :hg:`incoming` if you want to see what would have been added
3769 by a pull at the time you issued this command. If you then decide
3769 by a pull at the time you issued this command. If you then decide
3770 to add those changes to the repository, you should use :hg:`pull
3770 to add those changes to the repository, you should use :hg:`pull
3771 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3771 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
3772
3772
3773 If SOURCE is omitted, the 'default' path will be used.
3773 If SOURCE is omitted, the 'default' path will be used.
3774 See :hg:`help urls` for more information.
3774 See :hg:`help urls` for more information.
3775
3775
3776 Returns 0 on success, 1 if an update had unresolved files.
3776 Returns 0 on success, 1 if an update had unresolved files.
3777 """
3777 """
3778 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3778 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
3779 other = hg.peer(repo, opts, source)
3779 other = hg.peer(repo, opts, source)
3780 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3780 ui.status(_('pulling from %s\n') % util.hidepassword(source))
3781 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3781 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
3782
3782
3783 if opts.get('bookmark'):
3783 if opts.get('bookmark'):
3784 if not revs:
3784 if not revs:
3785 revs = []
3785 revs = []
3786 rb = other.listkeys('bookmarks')
3786 rb = other.listkeys('bookmarks')
3787 for b in opts['bookmark']:
3787 for b in opts['bookmark']:
3788 if b not in rb:
3788 if b not in rb:
3789 raise util.Abort(_('remote bookmark %s not found!') % b)
3789 raise util.Abort(_('remote bookmark %s not found!') % b)
3790 revs.append(rb[b])
3790 revs.append(rb[b])
3791
3791
3792 if revs:
3792 if revs:
3793 try:
3793 try:
3794 revs = [other.lookup(rev) for rev in revs]
3794 revs = [other.lookup(rev) for rev in revs]
3795 except error.CapabilityError:
3795 except error.CapabilityError:
3796 err = _("other repository doesn't support revision lookup, "
3796 err = _("other repository doesn't support revision lookup, "
3797 "so a rev cannot be specified.")
3797 "so a rev cannot be specified.")
3798 raise util.Abort(err)
3798 raise util.Abort(err)
3799
3799
3800 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
3800 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
3801 bookmarks.updatefromremote(ui, repo, other)
3801 bookmarks.updatefromremote(ui, repo, other)
3802 if checkout:
3802 if checkout:
3803 checkout = str(repo.changelog.rev(other.lookup(checkout)))
3803 checkout = str(repo.changelog.rev(other.lookup(checkout)))
3804 repo._subtoppath = source
3804 repo._subtoppath = source
3805 try:
3805 try:
3806 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
3806 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
3807
3807
3808 finally:
3808 finally:
3809 del repo._subtoppath
3809 del repo._subtoppath
3810
3810
3811 # update specified bookmarks
3811 # update specified bookmarks
3812 if opts.get('bookmark'):
3812 if opts.get('bookmark'):
3813 for b in opts['bookmark']:
3813 for b in opts['bookmark']:
3814 # explicit pull overrides local bookmark if any
3814 # explicit pull overrides local bookmark if any
3815 ui.status(_("importing bookmark %s\n") % b)
3815 ui.status(_("importing bookmark %s\n") % b)
3816 repo._bookmarks[b] = repo[rb[b]].node()
3816 repo._bookmarks[b] = repo[rb[b]].node()
3817 bookmarks.write(repo)
3817 bookmarks.write(repo)
3818
3818
3819 return ret
3819 return ret
3820
3820
3821 @command('^push',
3821 @command('^push',
3822 [('f', 'force', None, _('force push')),
3822 [('f', 'force', None, _('force push')),
3823 ('r', 'rev', [],
3823 ('r', 'rev', [],
3824 _('a changeset intended to be included in the destination'),
3824 _('a changeset intended to be included in the destination'),
3825 _('REV')),
3825 _('REV')),
3826 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
3826 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
3827 ('b', 'branch', [],
3827 ('b', 'branch', [],
3828 _('a specific branch you would like to push'), _('BRANCH')),
3828 _('a specific branch you would like to push'), _('BRANCH')),
3829 ('', 'new-branch', False, _('allow pushing a new branch')),
3829 ('', 'new-branch', False, _('allow pushing a new branch')),
3830 ] + remoteopts,
3830 ] + remoteopts,
3831 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
3831 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
3832 def push(ui, repo, dest=None, **opts):
3832 def push(ui, repo, dest=None, **opts):
3833 """push changes to the specified destination
3833 """push changes to the specified destination
3834
3834
3835 Push changesets from the local repository to the specified
3835 Push changesets from the local repository to the specified
3836 destination.
3836 destination.
3837
3837
3838 This operation is symmetrical to pull: it is identical to a pull
3838 This operation is symmetrical to pull: it is identical to a pull
3839 in the destination repository from the current one.
3839 in the destination repository from the current one.
3840
3840
3841 By default, push will not allow creation of new heads at the
3841 By default, push will not allow creation of new heads at the
3842 destination, since multiple heads would make it unclear which head
3842 destination, since multiple heads would make it unclear which head
3843 to use. In this situation, it is recommended to pull and merge
3843 to use. In this situation, it is recommended to pull and merge
3844 before pushing.
3844 before pushing.
3845
3845
3846 Use --new-branch if you want to allow push to create a new named
3846 Use --new-branch if you want to allow push to create a new named
3847 branch that is not present at the destination. This allows you to
3847 branch that is not present at the destination. This allows you to
3848 only create a new branch without forcing other changes.
3848 only create a new branch without forcing other changes.
3849
3849
3850 Use -f/--force to override the default behavior and push all
3850 Use -f/--force to override the default behavior and push all
3851 changesets on all branches.
3851 changesets on all branches.
3852
3852
3853 If -r/--rev is used, the specified revision and all its ancestors
3853 If -r/--rev is used, the specified revision and all its ancestors
3854 will be pushed to the remote repository.
3854 will be pushed to the remote repository.
3855
3855
3856 Please see :hg:`help urls` for important details about ``ssh://``
3856 Please see :hg:`help urls` for important details about ``ssh://``
3857 URLs. If DESTINATION is omitted, a default path will be used.
3857 URLs. If DESTINATION is omitted, a default path will be used.
3858
3858
3859 Returns 0 if push was successful, 1 if nothing to push.
3859 Returns 0 if push was successful, 1 if nothing to push.
3860 """
3860 """
3861
3861
3862 if opts.get('bookmark'):
3862 if opts.get('bookmark'):
3863 for b in opts['bookmark']:
3863 for b in opts['bookmark']:
3864 # translate -B options to -r so changesets get pushed
3864 # translate -B options to -r so changesets get pushed
3865 if b in repo._bookmarks:
3865 if b in repo._bookmarks:
3866 opts.setdefault('rev', []).append(b)
3866 opts.setdefault('rev', []).append(b)
3867 else:
3867 else:
3868 # if we try to push a deleted bookmark, translate it to null
3868 # if we try to push a deleted bookmark, translate it to null
3869 # this lets simultaneous -r, -b options continue working
3869 # this lets simultaneous -r, -b options continue working
3870 opts.setdefault('rev', []).append("null")
3870 opts.setdefault('rev', []).append("null")
3871
3871
3872 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3872 dest = ui.expandpath(dest or 'default-push', dest or 'default')
3873 dest, branches = hg.parseurl(dest, opts.get('branch'))
3873 dest, branches = hg.parseurl(dest, opts.get('branch'))
3874 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
3874 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
3875 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
3875 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
3876 other = hg.peer(repo, opts, dest)
3876 other = hg.peer(repo, opts, dest)
3877 if revs:
3877 if revs:
3878 revs = [repo.lookup(rev) for rev in revs]
3878 revs = [repo.lookup(rev) for rev in revs]
3879
3879
3880 repo._subtoppath = dest
3880 repo._subtoppath = dest
3881 try:
3881 try:
3882 # push subrepos depth-first for coherent ordering
3882 # push subrepos depth-first for coherent ordering
3883 c = repo['']
3883 c = repo['']
3884 subs = c.substate # only repos that are committed
3884 subs = c.substate # only repos that are committed
3885 for s in sorted(subs):
3885 for s in sorted(subs):
3886 if not c.sub(s).push(opts.get('force')):
3886 if not c.sub(s).push(opts.get('force')):
3887 return False
3887 return False
3888 finally:
3888 finally:
3889 del repo._subtoppath
3889 del repo._subtoppath
3890 result = repo.push(other, opts.get('force'), revs=revs,
3890 result = repo.push(other, opts.get('force'), revs=revs,
3891 newbranch=opts.get('new_branch'))
3891 newbranch=opts.get('new_branch'))
3892
3892
3893 result = (result == 0)
3893 result = (result == 0)
3894
3894
3895 if opts.get('bookmark'):
3895 if opts.get('bookmark'):
3896 rb = other.listkeys('bookmarks')
3896 rb = other.listkeys('bookmarks')
3897 for b in opts['bookmark']:
3897 for b in opts['bookmark']:
3898 # explicit push overrides remote bookmark if any
3898 # explicit push overrides remote bookmark if any
3899 if b in repo._bookmarks:
3899 if b in repo._bookmarks:
3900 ui.status(_("exporting bookmark %s\n") % b)
3900 ui.status(_("exporting bookmark %s\n") % b)
3901 new = repo[b].hex()
3901 new = repo[b].hex()
3902 elif b in rb:
3902 elif b in rb:
3903 ui.status(_("deleting remote bookmark %s\n") % b)
3903 ui.status(_("deleting remote bookmark %s\n") % b)
3904 new = '' # delete
3904 new = '' # delete
3905 else:
3905 else:
3906 ui.warn(_('bookmark %s does not exist on the local '
3906 ui.warn(_('bookmark %s does not exist on the local '
3907 'or remote repository!\n') % b)
3907 'or remote repository!\n') % b)
3908 return 2
3908 return 2
3909 old = rb.get(b, '')
3909 old = rb.get(b, '')
3910 r = other.pushkey('bookmarks', b, old, new)
3910 r = other.pushkey('bookmarks', b, old, new)
3911 if not r:
3911 if not r:
3912 ui.warn(_('updating bookmark %s failed!\n') % b)
3912 ui.warn(_('updating bookmark %s failed!\n') % b)
3913 if not result:
3913 if not result:
3914 result = 2
3914 result = 2
3915
3915
3916 return result
3916 return result
3917
3917
3918 @command('recover', [])
3918 @command('recover', [])
3919 def recover(ui, repo):
3919 def recover(ui, repo):
3920 """roll back an interrupted transaction
3920 """roll back an interrupted transaction
3921
3921
3922 Recover from an interrupted commit or pull.
3922 Recover from an interrupted commit or pull.
3923
3923
3924 This command tries to fix the repository status after an
3924 This command tries to fix the repository status after an
3925 interrupted operation. It should only be necessary when Mercurial
3925 interrupted operation. It should only be necessary when Mercurial
3926 suggests it.
3926 suggests it.
3927
3927
3928 Returns 0 if successful, 1 if nothing to recover or verify fails.
3928 Returns 0 if successful, 1 if nothing to recover or verify fails.
3929 """
3929 """
3930 if repo.recover():
3930 if repo.recover():
3931 return hg.verify(repo)
3931 return hg.verify(repo)
3932 return 1
3932 return 1
3933
3933
3934 @command('^remove|rm',
3934 @command('^remove|rm',
3935 [('A', 'after', None, _('record delete for missing files')),
3935 [('A', 'after', None, _('record delete for missing files')),
3936 ('f', 'force', None,
3936 ('f', 'force', None,
3937 _('remove (and delete) file even if added or modified')),
3937 _('remove (and delete) file even if added or modified')),
3938 ] + walkopts,
3938 ] + walkopts,
3939 _('[OPTION]... FILE...'))
3939 _('[OPTION]... FILE...'))
3940 def remove(ui, repo, *pats, **opts):
3940 def remove(ui, repo, *pats, **opts):
3941 """remove the specified files on the next commit
3941 """remove the specified files on the next commit
3942
3942
3943 Schedule the indicated files for removal from the repository.
3943 Schedule the indicated files for removal from the repository.
3944
3944
3945 This only removes files from the current branch, not from the
3945 This only removes files from the current branch, not from the
3946 entire project history. -A/--after can be used to remove only
3946 entire project history. -A/--after can be used to remove only
3947 files that have already been deleted, -f/--force can be used to
3947 files that have already been deleted, -f/--force can be used to
3948 force deletion, and -Af can be used to remove files from the next
3948 force deletion, and -Af can be used to remove files from the next
3949 revision without deleting them from the working directory.
3949 revision without deleting them from the working directory.
3950
3950
3951 The following table details the behavior of remove for different
3951 The following table details the behavior of remove for different
3952 file states (columns) and option combinations (rows). The file
3952 file states (columns) and option combinations (rows). The file
3953 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
3953 states are Added [A], Clean [C], Modified [M] and Missing [!] (as
3954 reported by :hg:`status`). The actions are Warn, Remove (from
3954 reported by :hg:`status`). The actions are Warn, Remove (from
3955 branch) and Delete (from disk):
3955 branch) and Delete (from disk):
3956
3956
3957 ======= == == == ==
3957 ======= == == == ==
3958 A C M !
3958 A C M !
3959 ======= == == == ==
3959 ======= == == == ==
3960 none W RD W R
3960 none W RD W R
3961 -f R RD RD R
3961 -f R RD RD R
3962 -A W W W R
3962 -A W W W R
3963 -Af R R R R
3963 -Af R R R R
3964 ======= == == == ==
3964 ======= == == == ==
3965
3965
3966 Note that remove never deletes files in Added [A] state from the
3966 Note that remove never deletes files in Added [A] state from the
3967 working directory, not even if option --force is specified.
3967 working directory, not even if option --force is specified.
3968
3968
3969 This command schedules the files to be removed at the next commit.
3969 This command schedules the files to be removed at the next commit.
3970 To undo a remove before that, see :hg:`revert`.
3970 To undo a remove before that, see :hg:`revert`.
3971
3971
3972 Returns 0 on success, 1 if any warnings encountered.
3972 Returns 0 on success, 1 if any warnings encountered.
3973 """
3973 """
3974
3974
3975 ret = 0
3975 ret = 0
3976 after, force = opts.get('after'), opts.get('force')
3976 after, force = opts.get('after'), opts.get('force')
3977 if not pats and not after:
3977 if not pats and not after:
3978 raise util.Abort(_('no files specified'))
3978 raise util.Abort(_('no files specified'))
3979
3979
3980 m = scmutil.match(repo[None], pats, opts)
3980 m = scmutil.match(repo[None], pats, opts)
3981 s = repo.status(match=m, clean=True)
3981 s = repo.status(match=m, clean=True)
3982 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
3982 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
3983
3983
3984 for f in m.files():
3984 for f in m.files():
3985 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
3985 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
3986 if os.path.exists(m.rel(f)):
3986 if os.path.exists(m.rel(f)):
3987 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
3987 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
3988 ret = 1
3988 ret = 1
3989
3989
3990 if force:
3990 if force:
3991 list = modified + deleted + clean + added
3991 list = modified + deleted + clean + added
3992 elif after:
3992 elif after:
3993 list = deleted
3993 list = deleted
3994 for f in modified + added + clean:
3994 for f in modified + added + clean:
3995 ui.warn(_('not removing %s: file still exists (use -f'
3995 ui.warn(_('not removing %s: file still exists (use -f'
3996 ' to force removal)\n') % m.rel(f))
3996 ' to force removal)\n') % m.rel(f))
3997 ret = 1
3997 ret = 1
3998 else:
3998 else:
3999 list = deleted + clean
3999 list = deleted + clean
4000 for f in modified:
4000 for f in modified:
4001 ui.warn(_('not removing %s: file is modified (use -f'
4001 ui.warn(_('not removing %s: file is modified (use -f'
4002 ' to force removal)\n') % m.rel(f))
4002 ' to force removal)\n') % m.rel(f))
4003 ret = 1
4003 ret = 1
4004 for f in added:
4004 for f in added:
4005 ui.warn(_('not removing %s: file has been marked for add (use -f'
4005 ui.warn(_('not removing %s: file has been marked for add (use -f'
4006 ' to force removal)\n') % m.rel(f))
4006 ' to force removal)\n') % m.rel(f))
4007 ret = 1
4007 ret = 1
4008
4008
4009 for f in sorted(list):
4009 for f in sorted(list):
4010 if ui.verbose or not m.exact(f):
4010 if ui.verbose or not m.exact(f):
4011 ui.status(_('removing %s\n') % m.rel(f))
4011 ui.status(_('removing %s\n') % m.rel(f))
4012
4012
4013 wlock = repo.wlock()
4013 wlock = repo.wlock()
4014 try:
4014 try:
4015 if not after:
4015 if not after:
4016 for f in list:
4016 for f in list:
4017 if f in added:
4017 if f in added:
4018 continue # we never unlink added files on remove
4018 continue # we never unlink added files on remove
4019 try:
4019 try:
4020 util.unlinkpath(repo.wjoin(f))
4020 util.unlinkpath(repo.wjoin(f))
4021 except OSError, inst:
4021 except OSError, inst:
4022 if inst.errno != errno.ENOENT:
4022 if inst.errno != errno.ENOENT:
4023 raise
4023 raise
4024 repo[None].forget(list)
4024 repo[None].forget(list)
4025 finally:
4025 finally:
4026 wlock.release()
4026 wlock.release()
4027
4027
4028 return ret
4028 return ret
4029
4029
4030 @command('rename|move|mv',
4030 @command('rename|move|mv',
4031 [('A', 'after', None, _('record a rename that has already occurred')),
4031 [('A', 'after', None, _('record a rename that has already occurred')),
4032 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4032 ('f', 'force', None, _('forcibly copy over an existing managed file')),
4033 ] + walkopts + dryrunopts,
4033 ] + walkopts + dryrunopts,
4034 _('[OPTION]... SOURCE... DEST'))
4034 _('[OPTION]... SOURCE... DEST'))
4035 def rename(ui, repo, *pats, **opts):
4035 def rename(ui, repo, *pats, **opts):
4036 """rename files; equivalent of copy + remove
4036 """rename files; equivalent of copy + remove
4037
4037
4038 Mark dest as copies of sources; mark sources for deletion. If dest
4038 Mark dest as copies of sources; mark sources for deletion. If dest
4039 is a directory, copies are put in that directory. If dest is a
4039 is a directory, copies are put in that directory. If dest is a
4040 file, there can only be one source.
4040 file, there can only be one source.
4041
4041
4042 By default, this command copies the contents of files as they
4042 By default, this command copies the contents of files as they
4043 exist in the working directory. If invoked with -A/--after, the
4043 exist in the working directory. If invoked with -A/--after, the
4044 operation is recorded, but no copying is performed.
4044 operation is recorded, but no copying is performed.
4045
4045
4046 This command takes effect at the next commit. To undo a rename
4046 This command takes effect at the next commit. To undo a rename
4047 before that, see :hg:`revert`.
4047 before that, see :hg:`revert`.
4048
4048
4049 Returns 0 on success, 1 if errors are encountered.
4049 Returns 0 on success, 1 if errors are encountered.
4050 """
4050 """
4051 wlock = repo.wlock(False)
4051 wlock = repo.wlock(False)
4052 try:
4052 try:
4053 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4053 return cmdutil.copy(ui, repo, pats, opts, rename=True)
4054 finally:
4054 finally:
4055 wlock.release()
4055 wlock.release()
4056
4056
4057 @command('resolve',
4057 @command('resolve',
4058 [('a', 'all', None, _('select all unresolved files')),
4058 [('a', 'all', None, _('select all unresolved files')),
4059 ('l', 'list', None, _('list state of files needing merge')),
4059 ('l', 'list', None, _('list state of files needing merge')),
4060 ('m', 'mark', None, _('mark files as resolved')),
4060 ('m', 'mark', None, _('mark files as resolved')),
4061 ('u', 'unmark', None, _('mark files as unresolved')),
4061 ('u', 'unmark', None, _('mark files as unresolved')),
4062 ('n', 'no-status', None, _('hide status prefix'))]
4062 ('n', 'no-status', None, _('hide status prefix'))]
4063 + mergetoolopts + walkopts,
4063 + mergetoolopts + walkopts,
4064 _('[OPTION]... [FILE]...'))
4064 _('[OPTION]... [FILE]...'))
4065 def resolve(ui, repo, *pats, **opts):
4065 def resolve(ui, repo, *pats, **opts):
4066 """redo merges or set/view the merge status of files
4066 """redo merges or set/view the merge status of files
4067
4067
4068 Merges with unresolved conflicts are often the result of
4068 Merges with unresolved conflicts are often the result of
4069 non-interactive merging using the ``internal:merge`` configuration
4069 non-interactive merging using the ``internal:merge`` configuration
4070 setting, or a command-line merge tool like ``diff3``. The resolve
4070 setting, or a command-line merge tool like ``diff3``. The resolve
4071 command is used to manage the files involved in a merge, after
4071 command is used to manage the files involved in a merge, after
4072 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4072 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
4073 working directory must have two parents).
4073 working directory must have two parents).
4074
4074
4075 The resolve command can be used in the following ways:
4075 The resolve command can be used in the following ways:
4076
4076
4077 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4077 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
4078 files, discarding any previous merge attempts. Re-merging is not
4078 files, discarding any previous merge attempts. Re-merging is not
4079 performed for files already marked as resolved. Use ``--all/-a``
4079 performed for files already marked as resolved. Use ``--all/-a``
4080 to select all unresolved files. ``--tool`` can be used to specify
4080 to select all unresolved files. ``--tool`` can be used to specify
4081 the merge tool used for the given files. It overrides the HGMERGE
4081 the merge tool used for the given files. It overrides the HGMERGE
4082 environment variable and your configuration files.
4082 environment variable and your configuration files.
4083
4083
4084 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4084 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
4085 (e.g. after having manually fixed-up the files). The default is
4085 (e.g. after having manually fixed-up the files). The default is
4086 to mark all unresolved files.
4086 to mark all unresolved files.
4087
4087
4088 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4088 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
4089 default is to mark all resolved files.
4089 default is to mark all resolved files.
4090
4090
4091 - :hg:`resolve -l`: list files which had or still have conflicts.
4091 - :hg:`resolve -l`: list files which had or still have conflicts.
4092 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4092 In the printed list, ``U`` = unresolved and ``R`` = resolved.
4093
4093
4094 Note that Mercurial will not let you commit files with unresolved
4094 Note that Mercurial will not let you commit files with unresolved
4095 merge conflicts. You must use :hg:`resolve -m ...` before you can
4095 merge conflicts. You must use :hg:`resolve -m ...` before you can
4096 commit after a conflicting merge.
4096 commit after a conflicting merge.
4097
4097
4098 Returns 0 on success, 1 if any files fail a resolve attempt.
4098 Returns 0 on success, 1 if any files fail a resolve attempt.
4099 """
4099 """
4100
4100
4101 all, mark, unmark, show, nostatus = \
4101 all, mark, unmark, show, nostatus = \
4102 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4102 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
4103
4103
4104 if (show and (mark or unmark)) or (mark and unmark):
4104 if (show and (mark or unmark)) or (mark and unmark):
4105 raise util.Abort(_("too many options specified"))
4105 raise util.Abort(_("too many options specified"))
4106 if pats and all:
4106 if pats and all:
4107 raise util.Abort(_("can't specify --all and patterns"))
4107 raise util.Abort(_("can't specify --all and patterns"))
4108 if not (all or pats or show or mark or unmark):
4108 if not (all or pats or show or mark or unmark):
4109 raise util.Abort(_('no files or directories specified; '
4109 raise util.Abort(_('no files or directories specified; '
4110 'use --all to remerge all files'))
4110 'use --all to remerge all files'))
4111
4111
4112 ms = mergemod.mergestate(repo)
4112 ms = mergemod.mergestate(repo)
4113 m = scmutil.match(repo[None], pats, opts)
4113 m = scmutil.match(repo[None], pats, opts)
4114 ret = 0
4114 ret = 0
4115
4115
4116 for f in ms:
4116 for f in ms:
4117 if m(f):
4117 if m(f):
4118 if show:
4118 if show:
4119 if nostatus:
4119 if nostatus:
4120 ui.write("%s\n" % f)
4120 ui.write("%s\n" % f)
4121 else:
4121 else:
4122 ui.write("%s %s\n" % (ms[f].upper(), f),
4122 ui.write("%s %s\n" % (ms[f].upper(), f),
4123 label='resolve.' +
4123 label='resolve.' +
4124 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4124 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
4125 elif mark:
4125 elif mark:
4126 ms.mark(f, "r")
4126 ms.mark(f, "r")
4127 elif unmark:
4127 elif unmark:
4128 ms.mark(f, "u")
4128 ms.mark(f, "u")
4129 else:
4129 else:
4130 wctx = repo[None]
4130 wctx = repo[None]
4131 mctx = wctx.parents()[-1]
4131 mctx = wctx.parents()[-1]
4132
4132
4133 # backup pre-resolve (merge uses .orig for its own purposes)
4133 # backup pre-resolve (merge uses .orig for its own purposes)
4134 a = repo.wjoin(f)
4134 a = repo.wjoin(f)
4135 util.copyfile(a, a + ".resolve")
4135 util.copyfile(a, a + ".resolve")
4136
4136
4137 try:
4137 try:
4138 # resolve file
4138 # resolve file
4139 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4139 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
4140 if ms.resolve(f, wctx, mctx):
4140 if ms.resolve(f, wctx, mctx):
4141 ret = 1
4141 ret = 1
4142 finally:
4142 finally:
4143 ui.setconfig('ui', 'forcemerge', '')
4143 ui.setconfig('ui', 'forcemerge', '')
4144
4144
4145 # replace filemerge's .orig file with our resolve file
4145 # replace filemerge's .orig file with our resolve file
4146 util.rename(a + ".resolve", a + ".orig")
4146 util.rename(a + ".resolve", a + ".orig")
4147
4147
4148 ms.commit()
4148 ms.commit()
4149 return ret
4149 return ret
4150
4150
4151 @command('revert',
4151 @command('revert',
4152 [('a', 'all', None, _('revert all changes when no arguments given')),
4152 [('a', 'all', None, _('revert all changes when no arguments given')),
4153 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4153 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
4154 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4154 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
4155 ('C', 'no-backup', None, _('do not save backup copies of files')),
4155 ('C', 'no-backup', None, _('do not save backup copies of files')),
4156 ] + walkopts + dryrunopts,
4156 ] + walkopts + dryrunopts,
4157 _('[OPTION]... [-r REV] [NAME]...'))
4157 _('[OPTION]... [-r REV] [NAME]...'))
4158 def revert(ui, repo, *pats, **opts):
4158 def revert(ui, repo, *pats, **opts):
4159 """restore files to their checkout state
4159 """restore files to their checkout state
4160
4160
4161 .. note::
4161 .. note::
4162 To check out earlier revisions, you should use :hg:`update REV`.
4162 To check out earlier revisions, you should use :hg:`update REV`.
4163 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4163 To cancel a merge (and lose your changes), use :hg:`update --clean .`.
4164
4164
4165 With no revision specified, revert the specified files or directories
4165 With no revision specified, revert the specified files or directories
4166 to the contents they had in the parent of the working directory.
4166 to the contents they had in the parent of the working directory.
4167 This restores the contents of files to an unmodified
4167 This restores the contents of files to an unmodified
4168 state and unschedules adds, removes, copies, and renames. If the
4168 state and unschedules adds, removes, copies, and renames. If the
4169 working directory has two parents, you must explicitly specify a
4169 working directory has two parents, you must explicitly specify a
4170 revision.
4170 revision.
4171
4171
4172 Using the -r/--rev or -d/--date options, revert the given files or
4172 Using the -r/--rev or -d/--date options, revert the given files or
4173 directories to their states as of a specific revision. Because
4173 directories to their states as of a specific revision. Because
4174 revert does not change the working directory parents, this will
4174 revert does not change the working directory parents, this will
4175 cause these files to appear modified. This can be helpful to "back
4175 cause these files to appear modified. This can be helpful to "back
4176 out" some or all of an earlier change. See :hg:`backout` for a
4176 out" some or all of an earlier change. See :hg:`backout` for a
4177 related method.
4177 related method.
4178
4178
4179 Modified files are saved with a .orig suffix before reverting.
4179 Modified files are saved with a .orig suffix before reverting.
4180 To disable these backups, use --no-backup.
4180 To disable these backups, use --no-backup.
4181
4181
4182 See :hg:`help dates` for a list of formats valid for -d/--date.
4182 See :hg:`help dates` for a list of formats valid for -d/--date.
4183
4183
4184 Returns 0 on success.
4184 Returns 0 on success.
4185 """
4185 """
4186
4186
4187 if opts.get("date"):
4187 if opts.get("date"):
4188 if opts.get("rev"):
4188 if opts.get("rev"):
4189 raise util.Abort(_("you can't specify a revision and a date"))
4189 raise util.Abort(_("you can't specify a revision and a date"))
4190 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4190 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
4191
4191
4192 parent, p2 = repo.dirstate.parents()
4192 parent, p2 = repo.dirstate.parents()
4193 if not opts.get('rev') and p2 != nullid:
4193 if not opts.get('rev') and p2 != nullid:
4194 # revert after merge is a trap for new users (issue2915)
4194 # revert after merge is a trap for new users (issue2915)
4195 raise util.Abort(_('uncommitted merge with no revision specified'),
4195 raise util.Abort(_('uncommitted merge with no revision specified'),
4196 hint=_('use "hg update" or see "hg help revert"'))
4196 hint=_('use "hg update" or see "hg help revert"'))
4197
4197
4198 ctx = scmutil.revsingle(repo, opts.get('rev'))
4198 ctx = scmutil.revsingle(repo, opts.get('rev'))
4199 node = ctx.node()
4199 node = ctx.node()
4200
4200
4201 if not pats and not opts.get('all'):
4201 if not pats and not opts.get('all'):
4202 msg = _("no files or directories specified")
4202 msg = _("no files or directories specified")
4203 if p2 != nullid:
4203 if p2 != nullid:
4204 hint = _("uncommitted merge, use --all to discard all changes,"
4204 hint = _("uncommitted merge, use --all to discard all changes,"
4205 " or 'hg update -C .' to abort the merge")
4205 " or 'hg update -C .' to abort the merge")
4206 raise util.Abort(msg, hint=hint)
4206 raise util.Abort(msg, hint=hint)
4207 dirty = util.any(repo.status())
4207 dirty = util.any(repo.status())
4208 if node != parent:
4208 if node != parent:
4209 if dirty:
4209 if dirty:
4210 hint = _("uncommitted changes, use --all to discard all"
4210 hint = _("uncommitted changes, use --all to discard all"
4211 " changes, or 'hg update %s' to update") % ctx.rev()
4211 " changes, or 'hg update %s' to update") % ctx.rev()
4212 else:
4212 else:
4213 hint = _("use --all to revert all files,"
4213 hint = _("use --all to revert all files,"
4214 " or 'hg update %s' to update") % ctx.rev()
4214 " or 'hg update %s' to update") % ctx.rev()
4215 elif dirty:
4215 elif dirty:
4216 hint = _("uncommitted changes, use --all to discard all changes")
4216 hint = _("uncommitted changes, use --all to discard all changes")
4217 else:
4217 else:
4218 hint = _("use --all to revert all files")
4218 hint = _("use --all to revert all files")
4219 raise util.Abort(msg, hint=hint)
4219 raise util.Abort(msg, hint=hint)
4220
4220
4221 mf = ctx.manifest()
4221 mf = ctx.manifest()
4222 if node == parent:
4222 if node == parent:
4223 pmf = mf
4223 pmf = mf
4224 else:
4224 else:
4225 pmf = None
4225 pmf = None
4226
4226
4227 # need all matching names in dirstate and manifest of target rev,
4227 # need all matching names in dirstate and manifest of target rev,
4228 # so have to walk both. do not print errors if files exist in one
4228 # so have to walk both. do not print errors if files exist in one
4229 # but not other.
4229 # but not other.
4230
4230
4231 names = {}
4231 names = {}
4232
4232
4233 wlock = repo.wlock()
4233 wlock = repo.wlock()
4234 try:
4234 try:
4235 # walk dirstate.
4235 # walk dirstate.
4236
4236
4237 m = scmutil.match(repo[None], pats, opts)
4237 m = scmutil.match(repo[None], pats, opts)
4238 m.bad = lambda x, y: False
4238 m.bad = lambda x, y: False
4239 for abs in repo.walk(m):
4239 for abs in repo.walk(m):
4240 names[abs] = m.rel(abs), m.exact(abs)
4240 names[abs] = m.rel(abs), m.exact(abs)
4241
4241
4242 # walk target manifest.
4242 # walk target manifest.
4243
4243
4244 def badfn(path, msg):
4244 def badfn(path, msg):
4245 if path in names:
4245 if path in names:
4246 return
4246 return
4247 path_ = path + '/'
4247 path_ = path + '/'
4248 for f in names:
4248 for f in names:
4249 if f.startswith(path_):
4249 if f.startswith(path_):
4250 return
4250 return
4251 ui.warn("%s: %s\n" % (m.rel(path), msg))
4251 ui.warn("%s: %s\n" % (m.rel(path), msg))
4252
4252
4253 m = scmutil.match(repo[node], pats, opts)
4253 m = scmutil.match(repo[node], pats, opts)
4254 m.bad = badfn
4254 m.bad = badfn
4255 for abs in repo[node].walk(m):
4255 for abs in repo[node].walk(m):
4256 if abs not in names:
4256 if abs not in names:
4257 names[abs] = m.rel(abs), m.exact(abs)
4257 names[abs] = m.rel(abs), m.exact(abs)
4258
4258
4259 m = scmutil.matchfiles(repo, names)
4259 m = scmutil.matchfiles(repo, names)
4260 changes = repo.status(match=m)[:4]
4260 changes = repo.status(match=m)[:4]
4261 modified, added, removed, deleted = map(set, changes)
4261 modified, added, removed, deleted = map(set, changes)
4262
4262
4263 # if f is a rename, also revert the source
4263 # if f is a rename, also revert the source
4264 cwd = repo.getcwd()
4264 cwd = repo.getcwd()
4265 for f in added:
4265 for f in added:
4266 src = repo.dirstate.copied(f)
4266 src = repo.dirstate.copied(f)
4267 if src and src not in names and repo.dirstate[src] == 'r':
4267 if src and src not in names and repo.dirstate[src] == 'r':
4268 removed.add(src)
4268 removed.add(src)
4269 names[src] = (repo.pathto(src, cwd), True)
4269 names[src] = (repo.pathto(src, cwd), True)
4270
4270
4271 def removeforget(abs):
4271 def removeforget(abs):
4272 if repo.dirstate[abs] == 'a':
4272 if repo.dirstate[abs] == 'a':
4273 return _('forgetting %s\n')
4273 return _('forgetting %s\n')
4274 return _('removing %s\n')
4274 return _('removing %s\n')
4275
4275
4276 revert = ([], _('reverting %s\n'))
4276 revert = ([], _('reverting %s\n'))
4277 add = ([], _('adding %s\n'))
4277 add = ([], _('adding %s\n'))
4278 remove = ([], removeforget)
4278 remove = ([], removeforget)
4279 undelete = ([], _('undeleting %s\n'))
4279 undelete = ([], _('undeleting %s\n'))
4280
4280
4281 disptable = (
4281 disptable = (
4282 # dispatch table:
4282 # dispatch table:
4283 # file state
4283 # file state
4284 # action if in target manifest
4284 # action if in target manifest
4285 # action if not in target manifest
4285 # action if not in target manifest
4286 # make backup if in target manifest
4286 # make backup if in target manifest
4287 # make backup if not in target manifest
4287 # make backup if not in target manifest
4288 (modified, revert, remove, True, True),
4288 (modified, revert, remove, True, True),
4289 (added, revert, remove, True, False),
4289 (added, revert, remove, True, False),
4290 (removed, undelete, None, False, False),
4290 (removed, undelete, None, False, False),
4291 (deleted, revert, remove, False, False),
4291 (deleted, revert, remove, False, False),
4292 )
4292 )
4293
4293
4294 for abs, (rel, exact) in sorted(names.items()):
4294 for abs, (rel, exact) in sorted(names.items()):
4295 mfentry = mf.get(abs)
4295 mfentry = mf.get(abs)
4296 target = repo.wjoin(abs)
4296 target = repo.wjoin(abs)
4297 def handle(xlist, dobackup):
4297 def handle(xlist, dobackup):
4298 xlist[0].append(abs)
4298 xlist[0].append(abs)
4299 if (dobackup and not opts.get('no_backup') and
4299 if (dobackup and not opts.get('no_backup') and
4300 os.path.lexists(target)):
4300 os.path.lexists(target)):
4301 bakname = "%s.orig" % rel
4301 bakname = "%s.orig" % rel
4302 ui.note(_('saving current version of %s as %s\n') %
4302 ui.note(_('saving current version of %s as %s\n') %
4303 (rel, bakname))
4303 (rel, bakname))
4304 if not opts.get('dry_run'):
4304 if not opts.get('dry_run'):
4305 util.rename(target, bakname)
4305 util.rename(target, bakname)
4306 if ui.verbose or not exact:
4306 if ui.verbose or not exact:
4307 msg = xlist[1]
4307 msg = xlist[1]
4308 if not isinstance(msg, basestring):
4308 if not isinstance(msg, basestring):
4309 msg = msg(abs)
4309 msg = msg(abs)
4310 ui.status(msg % rel)
4310 ui.status(msg % rel)
4311 for table, hitlist, misslist, backuphit, backupmiss in disptable:
4311 for table, hitlist, misslist, backuphit, backupmiss in disptable:
4312 if abs not in table:
4312 if abs not in table:
4313 continue
4313 continue
4314 # file has changed in dirstate
4314 # file has changed in dirstate
4315 if mfentry:
4315 if mfentry:
4316 handle(hitlist, backuphit)
4316 handle(hitlist, backuphit)
4317 elif misslist is not None:
4317 elif misslist is not None:
4318 handle(misslist, backupmiss)
4318 handle(misslist, backupmiss)
4319 break
4319 break
4320 else:
4320 else:
4321 if abs not in repo.dirstate:
4321 if abs not in repo.dirstate:
4322 if mfentry:
4322 if mfentry:
4323 handle(add, True)
4323 handle(add, True)
4324 elif exact:
4324 elif exact:
4325 ui.warn(_('file not managed: %s\n') % rel)
4325 ui.warn(_('file not managed: %s\n') % rel)
4326 continue
4326 continue
4327 # file has not changed in dirstate
4327 # file has not changed in dirstate
4328 if node == parent:
4328 if node == parent:
4329 if exact:
4329 if exact:
4330 ui.warn(_('no changes needed to %s\n') % rel)
4330 ui.warn(_('no changes needed to %s\n') % rel)
4331 continue
4331 continue
4332 if pmf is None:
4332 if pmf is None:
4333 # only need parent manifest in this unlikely case,
4333 # only need parent manifest in this unlikely case,
4334 # so do not read by default
4334 # so do not read by default
4335 pmf = repo[parent].manifest()
4335 pmf = repo[parent].manifest()
4336 if abs in pmf:
4336 if abs in pmf:
4337 if mfentry:
4337 if mfentry:
4338 # if version of file is same in parent and target
4338 # if version of file is same in parent and target
4339 # manifests, do nothing
4339 # manifests, do nothing
4340 if (pmf[abs] != mfentry or
4340 if (pmf[abs] != mfentry or
4341 pmf.flags(abs) != mf.flags(abs)):
4341 pmf.flags(abs) != mf.flags(abs)):
4342 handle(revert, False)
4342 handle(revert, False)
4343 else:
4343 else:
4344 handle(remove, False)
4344 handle(remove, False)
4345
4345
4346 if not opts.get('dry_run'):
4346 if not opts.get('dry_run'):
4347 def checkout(f):
4347 def checkout(f):
4348 fc = ctx[f]
4348 fc = ctx[f]
4349 repo.wwrite(f, fc.data(), fc.flags())
4349 repo.wwrite(f, fc.data(), fc.flags())
4350
4350
4351 audit_path = scmutil.pathauditor(repo.root)
4351 audit_path = scmutil.pathauditor(repo.root)
4352 for f in remove[0]:
4352 for f in remove[0]:
4353 if repo.dirstate[f] == 'a':
4353 if repo.dirstate[f] == 'a':
4354 repo.dirstate.drop(f)
4354 repo.dirstate.drop(f)
4355 continue
4355 continue
4356 audit_path(f)
4356 audit_path(f)
4357 try:
4357 try:
4358 util.unlinkpath(repo.wjoin(f))
4358 util.unlinkpath(repo.wjoin(f))
4359 except OSError:
4359 except OSError:
4360 pass
4360 pass
4361 repo.dirstate.remove(f)
4361 repo.dirstate.remove(f)
4362
4362
4363 normal = None
4363 normal = None
4364 if node == parent:
4364 if node == parent:
4365 # We're reverting to our parent. If possible, we'd like status
4365 # We're reverting to our parent. If possible, we'd like status
4366 # to report the file as clean. We have to use normallookup for
4366 # to report the file as clean. We have to use normallookup for
4367 # merges to avoid losing information about merged/dirty files.
4367 # merges to avoid losing information about merged/dirty files.
4368 if p2 != nullid:
4368 if p2 != nullid:
4369 normal = repo.dirstate.normallookup
4369 normal = repo.dirstate.normallookup
4370 else:
4370 else:
4371 normal = repo.dirstate.normal
4371 normal = repo.dirstate.normal
4372 for f in revert[0]:
4372 for f in revert[0]:
4373 checkout(f)
4373 checkout(f)
4374 if normal:
4374 if normal:
4375 normal(f)
4375 normal(f)
4376
4376
4377 for f in add[0]:
4377 for f in add[0]:
4378 checkout(f)
4378 checkout(f)
4379 repo.dirstate.add(f)
4379 repo.dirstate.add(f)
4380
4380
4381 normal = repo.dirstate.normallookup
4381 normal = repo.dirstate.normallookup
4382 if node == parent and p2 == nullid:
4382 if node == parent and p2 == nullid:
4383 normal = repo.dirstate.normal
4383 normal = repo.dirstate.normal
4384 for f in undelete[0]:
4384 for f in undelete[0]:
4385 checkout(f)
4385 checkout(f)
4386 normal(f)
4386 normal(f)
4387
4387
4388 finally:
4388 finally:
4389 wlock.release()
4389 wlock.release()
4390
4390
4391 @command('rollback', dryrunopts)
4391 @command('rollback', dryrunopts)
4392 def rollback(ui, repo, **opts):
4392 def rollback(ui, repo, **opts):
4393 """roll back the last transaction (dangerous)
4393 """roll back the last transaction (dangerous)
4394
4394
4395 This command should be used with care. There is only one level of
4395 This command should be used with care. There is only one level of
4396 rollback, and there is no way to undo a rollback. It will also
4396 rollback, and there is no way to undo a rollback. It will also
4397 restore the dirstate at the time of the last transaction, losing
4397 restore the dirstate at the time of the last transaction, losing
4398 any dirstate changes since that time. This command does not alter
4398 any dirstate changes since that time. This command does not alter
4399 the working directory.
4399 the working directory.
4400
4400
4401 Transactions are used to encapsulate the effects of all commands
4401 Transactions are used to encapsulate the effects of all commands
4402 that create new changesets or propagate existing changesets into a
4402 that create new changesets or propagate existing changesets into a
4403 repository. For example, the following commands are transactional,
4403 repository. For example, the following commands are transactional,
4404 and their effects can be rolled back:
4404 and their effects can be rolled back:
4405
4405
4406 - commit
4406 - commit
4407 - import
4407 - import
4408 - pull
4408 - pull
4409 - push (with this repository as the destination)
4409 - push (with this repository as the destination)
4410 - unbundle
4410 - unbundle
4411
4411
4412 This command is not intended for use on public repositories. Once
4412 This command is not intended for use on public repositories. Once
4413 changes are visible for pull by other users, rolling a transaction
4413 changes are visible for pull by other users, rolling a transaction
4414 back locally is ineffective (someone else may already have pulled
4414 back locally is ineffective (someone else may already have pulled
4415 the changes). Furthermore, a race is possible with readers of the
4415 the changes). Furthermore, a race is possible with readers of the
4416 repository; for example an in-progress pull from the repository
4416 repository; for example an in-progress pull from the repository
4417 may fail if a rollback is performed.
4417 may fail if a rollback is performed.
4418
4418
4419 Returns 0 on success, 1 if no rollback data is available.
4419 Returns 0 on success, 1 if no rollback data is available.
4420 """
4420 """
4421 return repo.rollback(opts.get('dry_run'))
4421 return repo.rollback(opts.get('dry_run'))
4422
4422
4423 @command('root', [])
4423 @command('root', [])
4424 def root(ui, repo):
4424 def root(ui, repo):
4425 """print the root (top) of the current working directory
4425 """print the root (top) of the current working directory
4426
4426
4427 Print the root directory of the current repository.
4427 Print the root directory of the current repository.
4428
4428
4429 Returns 0 on success.
4429 Returns 0 on success.
4430 """
4430 """
4431 ui.write(repo.root + "\n")
4431 ui.write(repo.root + "\n")
4432
4432
4433 @command('^serve',
4433 @command('^serve',
4434 [('A', 'accesslog', '', _('name of access log file to write to'),
4434 [('A', 'accesslog', '', _('name of access log file to write to'),
4435 _('FILE')),
4435 _('FILE')),
4436 ('d', 'daemon', None, _('run server in background')),
4436 ('d', 'daemon', None, _('run server in background')),
4437 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4437 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('NUM')),
4438 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4438 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
4439 # use string type, then we can check if something was passed
4439 # use string type, then we can check if something was passed
4440 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4440 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
4441 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4441 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
4442 _('ADDR')),
4442 _('ADDR')),
4443 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4443 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
4444 _('PREFIX')),
4444 _('PREFIX')),
4445 ('n', 'name', '',
4445 ('n', 'name', '',
4446 _('name to show in web pages (default: working directory)'), _('NAME')),
4446 _('name to show in web pages (default: working directory)'), _('NAME')),
4447 ('', 'web-conf', '',
4447 ('', 'web-conf', '',
4448 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
4448 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
4449 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4449 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
4450 _('FILE')),
4450 _('FILE')),
4451 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4451 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
4452 ('', 'stdio', None, _('for remote clients')),
4452 ('', 'stdio', None, _('for remote clients')),
4453 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
4453 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
4454 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4454 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
4455 ('', 'style', '', _('template style to use'), _('STYLE')),
4455 ('', 'style', '', _('template style to use'), _('STYLE')),
4456 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4456 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
4457 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
4457 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
4458 _('[OPTION]...'))
4458 _('[OPTION]...'))
4459 def serve(ui, repo, **opts):
4459 def serve(ui, repo, **opts):
4460 """start stand-alone webserver
4460 """start stand-alone webserver
4461
4461
4462 Start a local HTTP repository browser and pull server. You can use
4462 Start a local HTTP repository browser and pull server. You can use
4463 this for ad-hoc sharing and browsing of repositories. It is
4463 this for ad-hoc sharing and browsing of repositories. It is
4464 recommended to use a real web server to serve a repository for
4464 recommended to use a real web server to serve a repository for
4465 longer periods of time.
4465 longer periods of time.
4466
4466
4467 Please note that the server does not implement access control.
4467 Please note that the server does not implement access control.
4468 This means that, by default, anybody can read from the server and
4468 This means that, by default, anybody can read from the server and
4469 nobody can write to it by default. Set the ``web.allow_push``
4469 nobody can write to it by default. Set the ``web.allow_push``
4470 option to ``*`` to allow everybody to push to the server. You
4470 option to ``*`` to allow everybody to push to the server. You
4471 should use a real web server if you need to authenticate users.
4471 should use a real web server if you need to authenticate users.
4472
4472
4473 By default, the server logs accesses to stdout and errors to
4473 By default, the server logs accesses to stdout and errors to
4474 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4474 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
4475 files.
4475 files.
4476
4476
4477 To have the server choose a free port number to listen on, specify
4477 To have the server choose a free port number to listen on, specify
4478 a port number of 0; in this case, the server will print the port
4478 a port number of 0; in this case, the server will print the port
4479 number it uses.
4479 number it uses.
4480
4480
4481 Returns 0 on success.
4481 Returns 0 on success.
4482 """
4482 """
4483
4483
4484 if opts["stdio"] and opts["cmdserver"]:
4484 if opts["stdio"] and opts["cmdserver"]:
4485 raise util.Abort(_("cannot use --stdio with --cmdserver"))
4485 raise util.Abort(_("cannot use --stdio with --cmdserver"))
4486
4486
4487 def checkrepo():
4487 def checkrepo():
4488 if repo is None:
4488 if repo is None:
4489 raise error.RepoError(_("There is no Mercurial repository here"
4489 raise error.RepoError(_("There is no Mercurial repository here"
4490 " (.hg not found)"))
4490 " (.hg not found)"))
4491
4491
4492 if opts["stdio"]:
4492 if opts["stdio"]:
4493 checkrepo()
4493 checkrepo()
4494 s = sshserver.sshserver(ui, repo)
4494 s = sshserver.sshserver(ui, repo)
4495 s.serve_forever()
4495 s.serve_forever()
4496
4496
4497 if opts["cmdserver"]:
4497 if opts["cmdserver"]:
4498 checkrepo()
4498 checkrepo()
4499 s = commandserver.server(ui, repo, opts["cmdserver"])
4499 s = commandserver.server(ui, repo, opts["cmdserver"])
4500 return s.serve()
4500 return s.serve()
4501
4501
4502 # this way we can check if something was given in the command-line
4502 # this way we can check if something was given in the command-line
4503 if opts.get('port'):
4503 if opts.get('port'):
4504 opts['port'] = util.getport(opts.get('port'))
4504 opts['port'] = util.getport(opts.get('port'))
4505
4505
4506 baseui = repo and repo.baseui or ui
4506 baseui = repo and repo.baseui or ui
4507 optlist = ("name templates style address port prefix ipv6"
4507 optlist = ("name templates style address port prefix ipv6"
4508 " accesslog errorlog certificate encoding")
4508 " accesslog errorlog certificate encoding")
4509 for o in optlist.split():
4509 for o in optlist.split():
4510 val = opts.get(o, '')
4510 val = opts.get(o, '')
4511 if val in (None, ''): # should check against default options instead
4511 if val in (None, ''): # should check against default options instead
4512 continue
4512 continue
4513 baseui.setconfig("web", o, val)
4513 baseui.setconfig("web", o, val)
4514 if repo and repo.ui != baseui:
4514 if repo and repo.ui != baseui:
4515 repo.ui.setconfig("web", o, val)
4515 repo.ui.setconfig("web", o, val)
4516
4516
4517 o = opts.get('web_conf') or opts.get('webdir_conf')
4517 o = opts.get('web_conf') or opts.get('webdir_conf')
4518 if not o:
4518 if not o:
4519 if not repo:
4519 if not repo:
4520 raise error.RepoError(_("There is no Mercurial repository"
4520 raise error.RepoError(_("There is no Mercurial repository"
4521 " here (.hg not found)"))
4521 " here (.hg not found)"))
4522 o = repo.root
4522 o = repo.root
4523
4523
4524 app = hgweb.hgweb(o, baseui=ui)
4524 app = hgweb.hgweb(o, baseui=ui)
4525
4525
4526 class service(object):
4526 class service(object):
4527 def init(self):
4527 def init(self):
4528 util.setsignalhandler()
4528 util.setsignalhandler()
4529 self.httpd = hgweb.server.create_server(ui, app)
4529 self.httpd = hgweb.server.create_server(ui, app)
4530
4530
4531 if opts['port'] and not ui.verbose:
4531 if opts['port'] and not ui.verbose:
4532 return
4532 return
4533
4533
4534 if self.httpd.prefix:
4534 if self.httpd.prefix:
4535 prefix = self.httpd.prefix.strip('/') + '/'
4535 prefix = self.httpd.prefix.strip('/') + '/'
4536 else:
4536 else:
4537 prefix = ''
4537 prefix = ''
4538
4538
4539 port = ':%d' % self.httpd.port
4539 port = ':%d' % self.httpd.port
4540 if port == ':80':
4540 if port == ':80':
4541 port = ''
4541 port = ''
4542
4542
4543 bindaddr = self.httpd.addr
4543 bindaddr = self.httpd.addr
4544 if bindaddr == '0.0.0.0':
4544 if bindaddr == '0.0.0.0':
4545 bindaddr = '*'
4545 bindaddr = '*'
4546 elif ':' in bindaddr: # IPv6
4546 elif ':' in bindaddr: # IPv6
4547 bindaddr = '[%s]' % bindaddr
4547 bindaddr = '[%s]' % bindaddr
4548
4548
4549 fqaddr = self.httpd.fqaddr
4549 fqaddr = self.httpd.fqaddr
4550 if ':' in fqaddr:
4550 if ':' in fqaddr:
4551 fqaddr = '[%s]' % fqaddr
4551 fqaddr = '[%s]' % fqaddr
4552 if opts['port']:
4552 if opts['port']:
4553 write = ui.status
4553 write = ui.status
4554 else:
4554 else:
4555 write = ui.write
4555 write = ui.write
4556 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
4556 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
4557 (fqaddr, port, prefix, bindaddr, self.httpd.port))
4557 (fqaddr, port, prefix, bindaddr, self.httpd.port))
4558
4558
4559 def run(self):
4559 def run(self):
4560 self.httpd.serve_forever()
4560 self.httpd.serve_forever()
4561
4561
4562 service = service()
4562 service = service()
4563
4563
4564 cmdutil.service(opts, initfn=service.init, runfn=service.run)
4564 cmdutil.service(opts, initfn=service.init, runfn=service.run)
4565
4565
4566 @command('showconfig|debugconfig',
4566 @command('showconfig|debugconfig',
4567 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4567 [('u', 'untrusted', None, _('show untrusted configuration options'))],
4568 _('[-u] [NAME]...'))
4568 _('[-u] [NAME]...'))
4569 def showconfig(ui, repo, *values, **opts):
4569 def showconfig(ui, repo, *values, **opts):
4570 """show combined config settings from all hgrc files
4570 """show combined config settings from all hgrc files
4571
4571
4572 With no arguments, print names and values of all config items.
4572 With no arguments, print names and values of all config items.
4573
4573
4574 With one argument of the form section.name, print just the value
4574 With one argument of the form section.name, print just the value
4575 of that config item.
4575 of that config item.
4576
4576
4577 With multiple arguments, print names and values of all config
4577 With multiple arguments, print names and values of all config
4578 items with matching section names.
4578 items with matching section names.
4579
4579
4580 With --debug, the source (filename and line number) is printed
4580 With --debug, the source (filename and line number) is printed
4581 for each config item.
4581 for each config item.
4582
4582
4583 Returns 0 on success.
4583 Returns 0 on success.
4584 """
4584 """
4585
4585
4586 for f in scmutil.rcpath():
4586 for f in scmutil.rcpath():
4587 ui.debug('read config from: %s\n' % f)
4587 ui.debug('read config from: %s\n' % f)
4588 untrusted = bool(opts.get('untrusted'))
4588 untrusted = bool(opts.get('untrusted'))
4589 if values:
4589 if values:
4590 sections = [v for v in values if '.' not in v]
4590 sections = [v for v in values if '.' not in v]
4591 items = [v for v in values if '.' in v]
4591 items = [v for v in values if '.' in v]
4592 if len(items) > 1 or items and sections:
4592 if len(items) > 1 or items and sections:
4593 raise util.Abort(_('only one config item permitted'))
4593 raise util.Abort(_('only one config item permitted'))
4594 for section, name, value in ui.walkconfig(untrusted=untrusted):
4594 for section, name, value in ui.walkconfig(untrusted=untrusted):
4595 value = str(value).replace('\n', '\\n')
4595 value = str(value).replace('\n', '\\n')
4596 sectname = section + '.' + name
4596 sectname = section + '.' + name
4597 if values:
4597 if values:
4598 for v in values:
4598 for v in values:
4599 if v == section:
4599 if v == section:
4600 ui.debug('%s: ' %
4600 ui.debug('%s: ' %
4601 ui.configsource(section, name, untrusted))
4601 ui.configsource(section, name, untrusted))
4602 ui.write('%s=%s\n' % (sectname, value))
4602 ui.write('%s=%s\n' % (sectname, value))
4603 elif v == sectname:
4603 elif v == sectname:
4604 ui.debug('%s: ' %
4604 ui.debug('%s: ' %
4605 ui.configsource(section, name, untrusted))
4605 ui.configsource(section, name, untrusted))
4606 ui.write(value, '\n')
4606 ui.write(value, '\n')
4607 else:
4607 else:
4608 ui.debug('%s: ' %
4608 ui.debug('%s: ' %
4609 ui.configsource(section, name, untrusted))
4609 ui.configsource(section, name, untrusted))
4610 ui.write('%s=%s\n' % (sectname, value))
4610 ui.write('%s=%s\n' % (sectname, value))
4611
4611
4612 @command('^status|st',
4612 @command('^status|st',
4613 [('A', 'all', None, _('show status of all files')),
4613 [('A', 'all', None, _('show status of all files')),
4614 ('m', 'modified', None, _('show only modified files')),
4614 ('m', 'modified', None, _('show only modified files')),
4615 ('a', 'added', None, _('show only added files')),
4615 ('a', 'added', None, _('show only added files')),
4616 ('r', 'removed', None, _('show only removed files')),
4616 ('r', 'removed', None, _('show only removed files')),
4617 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4617 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
4618 ('c', 'clean', None, _('show only files without changes')),
4618 ('c', 'clean', None, _('show only files without changes')),
4619 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4619 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
4620 ('i', 'ignored', None, _('show only ignored files')),
4620 ('i', 'ignored', None, _('show only ignored files')),
4621 ('n', 'no-status', None, _('hide status prefix')),
4621 ('n', 'no-status', None, _('hide status prefix')),
4622 ('C', 'copies', None, _('show source of copied files')),
4622 ('C', 'copies', None, _('show source of copied files')),
4623 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4623 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4624 ('', 'rev', [], _('show difference from revision'), _('REV')),
4624 ('', 'rev', [], _('show difference from revision'), _('REV')),
4625 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4625 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
4626 ] + walkopts + subrepoopts,
4626 ] + walkopts + subrepoopts,
4627 _('[OPTION]... [FILE]...'))
4627 _('[OPTION]... [FILE]...'))
4628 def status(ui, repo, *pats, **opts):
4628 def status(ui, repo, *pats, **opts):
4629 """show changed files in the working directory
4629 """show changed files in the working directory
4630
4630
4631 Show status of files in the repository. If names are given, only
4631 Show status of files in the repository. If names are given, only
4632 files that match are shown. Files that are clean or ignored or
4632 files that match are shown. Files that are clean or ignored or
4633 the source of a copy/move operation, are not listed unless
4633 the source of a copy/move operation, are not listed unless
4634 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4634 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
4635 Unless options described with "show only ..." are given, the
4635 Unless options described with "show only ..." are given, the
4636 options -mardu are used.
4636 options -mardu are used.
4637
4637
4638 Option -q/--quiet hides untracked (unknown and ignored) files
4638 Option -q/--quiet hides untracked (unknown and ignored) files
4639 unless explicitly requested with -u/--unknown or -i/--ignored.
4639 unless explicitly requested with -u/--unknown or -i/--ignored.
4640
4640
4641 .. note::
4641 .. note::
4642 status may appear to disagree with diff if permissions have
4642 status may appear to disagree with diff if permissions have
4643 changed or a merge has occurred. The standard diff format does
4643 changed or a merge has occurred. The standard diff format does
4644 not report permission changes and diff only reports changes
4644 not report permission changes and diff only reports changes
4645 relative to one merge parent.
4645 relative to one merge parent.
4646
4646
4647 If one revision is given, it is used as the base revision.
4647 If one revision is given, it is used as the base revision.
4648 If two revisions are given, the differences between them are
4648 If two revisions are given, the differences between them are
4649 shown. The --change option can also be used as a shortcut to list
4649 shown. The --change option can also be used as a shortcut to list
4650 the changed files of a revision from its first parent.
4650 the changed files of a revision from its first parent.
4651
4651
4652 The codes used to show the status of files are::
4652 The codes used to show the status of files are::
4653
4653
4654 M = modified
4654 M = modified
4655 A = added
4655 A = added
4656 R = removed
4656 R = removed
4657 C = clean
4657 C = clean
4658 ! = missing (deleted by non-hg command, but still tracked)
4658 ! = missing (deleted by non-hg command, but still tracked)
4659 ? = not tracked
4659 ? = not tracked
4660 I = ignored
4660 I = ignored
4661 = origin of the previous file listed as A (added)
4661 = origin of the previous file listed as A (added)
4662
4662
4663 Returns 0 on success.
4663 Returns 0 on success.
4664 """
4664 """
4665
4665
4666 revs = opts.get('rev')
4666 revs = opts.get('rev')
4667 change = opts.get('change')
4667 change = opts.get('change')
4668
4668
4669 if revs and change:
4669 if revs and change:
4670 msg = _('cannot specify --rev and --change at the same time')
4670 msg = _('cannot specify --rev and --change at the same time')
4671 raise util.Abort(msg)
4671 raise util.Abort(msg)
4672 elif change:
4672 elif change:
4673 node2 = repo.lookup(change)
4673 node2 = repo.lookup(change)
4674 node1 = repo[node2].p1().node()
4674 node1 = repo[node2].p1().node()
4675 else:
4675 else:
4676 node1, node2 = scmutil.revpair(repo, revs)
4676 node1, node2 = scmutil.revpair(repo, revs)
4677
4677
4678 cwd = (pats and repo.getcwd()) or ''
4678 cwd = (pats and repo.getcwd()) or ''
4679 end = opts.get('print0') and '\0' or '\n'
4679 end = opts.get('print0') and '\0' or '\n'
4680 copy = {}
4680 copy = {}
4681 states = 'modified added removed deleted unknown ignored clean'.split()
4681 states = 'modified added removed deleted unknown ignored clean'.split()
4682 show = [k for k in states if opts.get(k)]
4682 show = [k for k in states if opts.get(k)]
4683 if opts.get('all'):
4683 if opts.get('all'):
4684 show += ui.quiet and (states[:4] + ['clean']) or states
4684 show += ui.quiet and (states[:4] + ['clean']) or states
4685 if not show:
4685 if not show:
4686 show = ui.quiet and states[:4] or states[:5]
4686 show = ui.quiet and states[:4] or states[:5]
4687
4687
4688 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
4688 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
4689 'ignored' in show, 'clean' in show, 'unknown' in show,
4689 'ignored' in show, 'clean' in show, 'unknown' in show,
4690 opts.get('subrepos'))
4690 opts.get('subrepos'))
4691 changestates = zip(states, 'MAR!?IC', stat)
4691 changestates = zip(states, 'MAR!?IC', stat)
4692
4692
4693 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
4693 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
4694 ctxn = repo[nullid]
4694 ctxn = repo[nullid]
4695 ctx1 = repo[node1]
4695 ctx1 = repo[node1]
4696 ctx2 = repo[node2]
4696 ctx2 = repo[node2]
4697 added = stat[1]
4697 added = stat[1]
4698 if node2 is None:
4698 if node2 is None:
4699 added = stat[0] + stat[1] # merged?
4699 added = stat[0] + stat[1] # merged?
4700
4700
4701 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
4701 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
4702 if k in added:
4702 if k in added:
4703 copy[k] = v
4703 copy[k] = v
4704 elif v in added:
4704 elif v in added:
4705 copy[v] = k
4705 copy[v] = k
4706
4706
4707 for state, char, files in changestates:
4707 for state, char, files in changestates:
4708 if state in show:
4708 if state in show:
4709 format = "%s %%s%s" % (char, end)
4709 format = "%s %%s%s" % (char, end)
4710 if opts.get('no_status'):
4710 if opts.get('no_status'):
4711 format = "%%s%s" % end
4711 format = "%%s%s" % end
4712
4712
4713 for f in files:
4713 for f in files:
4714 ui.write(format % repo.pathto(f, cwd),
4714 ui.write(format % repo.pathto(f, cwd),
4715 label='status.' + state)
4715 label='status.' + state)
4716 if f in copy:
4716 if f in copy:
4717 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
4717 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end),
4718 label='status.copied')
4718 label='status.copied')
4719
4719
4720 @command('^summary|sum',
4720 @command('^summary|sum',
4721 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4721 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
4722 def summary(ui, repo, **opts):
4722 def summary(ui, repo, **opts):
4723 """summarize working directory state
4723 """summarize working directory state
4724
4724
4725 This generates a brief summary of the working directory state,
4725 This generates a brief summary of the working directory state,
4726 including parents, branch, commit status, and available updates.
4726 including parents, branch, commit status, and available updates.
4727
4727
4728 With the --remote option, this will check the default paths for
4728 With the --remote option, this will check the default paths for
4729 incoming and outgoing changes. This can be time-consuming.
4729 incoming and outgoing changes. This can be time-consuming.
4730
4730
4731 Returns 0 on success.
4731 Returns 0 on success.
4732 """
4732 """
4733
4733
4734 ctx = repo[None]
4734 ctx = repo[None]
4735 parents = ctx.parents()
4735 parents = ctx.parents()
4736 pnode = parents[0].node()
4736 pnode = parents[0].node()
4737 marks = []
4737 marks = []
4738
4738
4739 for p in parents:
4739 for p in parents:
4740 # label with log.changeset (instead of log.parent) since this
4740 # label with log.changeset (instead of log.parent) since this
4741 # shows a working directory parent *changeset*:
4741 # shows a working directory parent *changeset*:
4742 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
4742 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
4743 label='log.changeset')
4743 label='log.changeset')
4744 ui.write(' '.join(p.tags()), label='log.tag')
4744 ui.write(' '.join(p.tags()), label='log.tag')
4745 if p.bookmarks():
4745 if p.bookmarks():
4746 marks.extend(p.bookmarks())
4746 marks.extend(p.bookmarks())
4747 if p.rev() == -1:
4747 if p.rev() == -1:
4748 if not len(repo):
4748 if not len(repo):
4749 ui.write(_(' (empty repository)'))
4749 ui.write(_(' (empty repository)'))
4750 else:
4750 else:
4751 ui.write(_(' (no revision checked out)'))
4751 ui.write(_(' (no revision checked out)'))
4752 ui.write('\n')
4752 ui.write('\n')
4753 if p.description():
4753 if p.description():
4754 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4754 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
4755 label='log.summary')
4755 label='log.summary')
4756
4756
4757 branch = ctx.branch()
4757 branch = ctx.branch()
4758 bheads = repo.branchheads(branch)
4758 bheads = repo.branchheads(branch)
4759 m = _('branch: %s\n') % branch
4759 m = _('branch: %s\n') % branch
4760 if branch != 'default':
4760 if branch != 'default':
4761 ui.write(m, label='log.branch')
4761 ui.write(m, label='log.branch')
4762 else:
4762 else:
4763 ui.status(m, label='log.branch')
4763 ui.status(m, label='log.branch')
4764
4764
4765 if marks:
4765 if marks:
4766 current = repo._bookmarkcurrent
4766 current = repo._bookmarkcurrent
4767 ui.write(_('bookmarks:'), label='log.bookmark')
4767 ui.write(_('bookmarks:'), label='log.bookmark')
4768 if current is not None:
4768 if current is not None:
4769 try:
4769 try:
4770 marks.remove(current)
4770 marks.remove(current)
4771 ui.write(' *' + current, label='bookmarks.current')
4771 ui.write(' *' + current, label='bookmarks.current')
4772 except ValueError:
4772 except ValueError:
4773 # current bookmark not in parent ctx marks
4773 # current bookmark not in parent ctx marks
4774 pass
4774 pass
4775 for m in marks:
4775 for m in marks:
4776 ui.write(' ' + m, label='log.bookmark')
4776 ui.write(' ' + m, label='log.bookmark')
4777 ui.write('\n', label='log.bookmark')
4777 ui.write('\n', label='log.bookmark')
4778
4778
4779 st = list(repo.status(unknown=True))[:6]
4779 st = list(repo.status(unknown=True))[:6]
4780
4780
4781 c = repo.dirstate.copies()
4781 c = repo.dirstate.copies()
4782 copied, renamed = [], []
4782 copied, renamed = [], []
4783 for d, s in c.iteritems():
4783 for d, s in c.iteritems():
4784 if s in st[2]:
4784 if s in st[2]:
4785 st[2].remove(s)
4785 st[2].remove(s)
4786 renamed.append(d)
4786 renamed.append(d)
4787 else:
4787 else:
4788 copied.append(d)
4788 copied.append(d)
4789 if d in st[1]:
4789 if d in st[1]:
4790 st[1].remove(d)
4790 st[1].remove(d)
4791 st.insert(3, renamed)
4791 st.insert(3, renamed)
4792 st.insert(4, copied)
4792 st.insert(4, copied)
4793
4793
4794 ms = mergemod.mergestate(repo)
4794 ms = mergemod.mergestate(repo)
4795 st.append([f for f in ms if ms[f] == 'u'])
4795 st.append([f for f in ms if ms[f] == 'u'])
4796
4796
4797 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4797 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
4798 st.append(subs)
4798 st.append(subs)
4799
4799
4800 labels = [ui.label(_('%d modified'), 'status.modified'),
4800 labels = [ui.label(_('%d modified'), 'status.modified'),
4801 ui.label(_('%d added'), 'status.added'),
4801 ui.label(_('%d added'), 'status.added'),
4802 ui.label(_('%d removed'), 'status.removed'),
4802 ui.label(_('%d removed'), 'status.removed'),
4803 ui.label(_('%d renamed'), 'status.copied'),
4803 ui.label(_('%d renamed'), 'status.copied'),
4804 ui.label(_('%d copied'), 'status.copied'),
4804 ui.label(_('%d copied'), 'status.copied'),
4805 ui.label(_('%d deleted'), 'status.deleted'),
4805 ui.label(_('%d deleted'), 'status.deleted'),
4806 ui.label(_('%d unknown'), 'status.unknown'),
4806 ui.label(_('%d unknown'), 'status.unknown'),
4807 ui.label(_('%d ignored'), 'status.ignored'),
4807 ui.label(_('%d ignored'), 'status.ignored'),
4808 ui.label(_('%d unresolved'), 'resolve.unresolved'),
4808 ui.label(_('%d unresolved'), 'resolve.unresolved'),
4809 ui.label(_('%d subrepos'), 'status.modified')]
4809 ui.label(_('%d subrepos'), 'status.modified')]
4810 t = []
4810 t = []
4811 for s, l in zip(st, labels):
4811 for s, l in zip(st, labels):
4812 if s:
4812 if s:
4813 t.append(l % len(s))
4813 t.append(l % len(s))
4814
4814
4815 t = ', '.join(t)
4815 t = ', '.join(t)
4816 cleanworkdir = False
4816 cleanworkdir = False
4817
4817
4818 if len(parents) > 1:
4818 if len(parents) > 1:
4819 t += _(' (merge)')
4819 t += _(' (merge)')
4820 elif branch != parents[0].branch():
4820 elif branch != parents[0].branch():
4821 t += _(' (new branch)')
4821 t += _(' (new branch)')
4822 elif (parents[0].extra().get('close') and
4822 elif (parents[0].extra().get('close') and
4823 pnode in repo.branchheads(branch, closed=True)):
4823 pnode in repo.branchheads(branch, closed=True)):
4824 t += _(' (head closed)')
4824 t += _(' (head closed)')
4825 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
4825 elif not (st[0] or st[1] or st[2] or st[3] or st[4] or st[9]):
4826 t += _(' (clean)')
4826 t += _(' (clean)')
4827 cleanworkdir = True
4827 cleanworkdir = True
4828 elif pnode not in bheads:
4828 elif pnode not in bheads:
4829 t += _(' (new branch head)')
4829 t += _(' (new branch head)')
4830
4830
4831 if cleanworkdir:
4831 if cleanworkdir:
4832 ui.status(_('commit: %s\n') % t.strip())
4832 ui.status(_('commit: %s\n') % t.strip())
4833 else:
4833 else:
4834 ui.write(_('commit: %s\n') % t.strip())
4834 ui.write(_('commit: %s\n') % t.strip())
4835
4835
4836 # all ancestors of branch heads - all ancestors of parent = new csets
4836 # all ancestors of branch heads - all ancestors of parent = new csets
4837 new = [0] * len(repo)
4837 new = [0] * len(repo)
4838 cl = repo.changelog
4838 cl = repo.changelog
4839 for a in [cl.rev(n) for n in bheads]:
4839 for a in [cl.rev(n) for n in bheads]:
4840 new[a] = 1
4840 new[a] = 1
4841 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
4841 for a in cl.ancestors(*[cl.rev(n) for n in bheads]):
4842 new[a] = 1
4842 new[a] = 1
4843 for a in [p.rev() for p in parents]:
4843 for a in [p.rev() for p in parents]:
4844 if a >= 0:
4844 if a >= 0:
4845 new[a] = 0
4845 new[a] = 0
4846 for a in cl.ancestors(*[p.rev() for p in parents]):
4846 for a in cl.ancestors(*[p.rev() for p in parents]):
4847 new[a] = 0
4847 new[a] = 0
4848 new = sum(new)
4848 new = sum(new)
4849
4849
4850 if new == 0:
4850 if new == 0:
4851 ui.status(_('update: (current)\n'))
4851 ui.status(_('update: (current)\n'))
4852 elif pnode not in bheads:
4852 elif pnode not in bheads:
4853 ui.write(_('update: %d new changesets (update)\n') % new)
4853 ui.write(_('update: %d new changesets (update)\n') % new)
4854 else:
4854 else:
4855 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4855 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
4856 (new, len(bheads)))
4856 (new, len(bheads)))
4857
4857
4858 if opts.get('remote'):
4858 if opts.get('remote'):
4859 t = []
4859 t = []
4860 source, branches = hg.parseurl(ui.expandpath('default'))
4860 source, branches = hg.parseurl(ui.expandpath('default'))
4861 other = hg.peer(repo, {}, source)
4861 other = hg.peer(repo, {}, source)
4862 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4862 revs, checkout = hg.addbranchrevs(repo, other, branches, opts.get('rev'))
4863 ui.debug('comparing with %s\n' % util.hidepassword(source))
4863 ui.debug('comparing with %s\n' % util.hidepassword(source))
4864 repo.ui.pushbuffer()
4864 repo.ui.pushbuffer()
4865 commoninc = discovery.findcommonincoming(repo, other)
4865 commoninc = discovery.findcommonincoming(repo, other)
4866 _common, incoming, _rheads = commoninc
4866 _common, incoming, _rheads = commoninc
4867 repo.ui.popbuffer()
4867 repo.ui.popbuffer()
4868 if incoming:
4868 if incoming:
4869 t.append(_('1 or more incoming'))
4869 t.append(_('1 or more incoming'))
4870
4870
4871 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
4871 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
4872 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
4872 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
4873 if source != dest:
4873 if source != dest:
4874 other = hg.peer(repo, {}, dest)
4874 other = hg.peer(repo, {}, dest)
4875 commoninc = None
4875 commoninc = None
4876 ui.debug('comparing with %s\n' % util.hidepassword(dest))
4876 ui.debug('comparing with %s\n' % util.hidepassword(dest))
4877 repo.ui.pushbuffer()
4877 repo.ui.pushbuffer()
4878 common, outheads = discovery.findcommonoutgoing(repo, other,
4878 common, outheads = discovery.findcommonoutgoing(repo, other,
4879 commoninc=commoninc)
4879 commoninc=commoninc)
4880 repo.ui.popbuffer()
4880 repo.ui.popbuffer()
4881 o = repo.changelog.findmissing(common=common, heads=outheads)
4881 o = repo.changelog.findmissing(common=common, heads=outheads)
4882 if o:
4882 if o:
4883 t.append(_('%d outgoing') % len(o))
4883 t.append(_('%d outgoing') % len(o))
4884 if 'bookmarks' in other.listkeys('namespaces'):
4884 if 'bookmarks' in other.listkeys('namespaces'):
4885 lmarks = repo.listkeys('bookmarks')
4885 lmarks = repo.listkeys('bookmarks')
4886 rmarks = other.listkeys('bookmarks')
4886 rmarks = other.listkeys('bookmarks')
4887 diff = set(rmarks) - set(lmarks)
4887 diff = set(rmarks) - set(lmarks)
4888 if len(diff) > 0:
4888 if len(diff) > 0:
4889 t.append(_('%d incoming bookmarks') % len(diff))
4889 t.append(_('%d incoming bookmarks') % len(diff))
4890 diff = set(lmarks) - set(rmarks)
4890 diff = set(lmarks) - set(rmarks)
4891 if len(diff) > 0:
4891 if len(diff) > 0:
4892 t.append(_('%d outgoing bookmarks') % len(diff))
4892 t.append(_('%d outgoing bookmarks') % len(diff))
4893
4893
4894 if t:
4894 if t:
4895 ui.write(_('remote: %s\n') % (', '.join(t)))
4895 ui.write(_('remote: %s\n') % (', '.join(t)))
4896 else:
4896 else:
4897 ui.status(_('remote: (synced)\n'))
4897 ui.status(_('remote: (synced)\n'))
4898
4898
4899 @command('tag',
4899 @command('tag',
4900 [('f', 'force', None, _('force tag')),
4900 [('f', 'force', None, _('force tag')),
4901 ('l', 'local', None, _('make the tag local')),
4901 ('l', 'local', None, _('make the tag local')),
4902 ('r', 'rev', '', _('revision to tag'), _('REV')),
4902 ('r', 'rev', '', _('revision to tag'), _('REV')),
4903 ('', 'remove', None, _('remove a tag')),
4903 ('', 'remove', None, _('remove a tag')),
4904 # -l/--local is already there, commitopts cannot be used
4904 # -l/--local is already there, commitopts cannot be used
4905 ('e', 'edit', None, _('edit commit message')),
4905 ('e', 'edit', None, _('edit commit message')),
4906 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
4906 ('m', 'message', '', _('use <text> as commit message'), _('TEXT')),
4907 ] + commitopts2,
4907 ] + commitopts2,
4908 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
4908 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
4909 def tag(ui, repo, name1, *names, **opts):
4909 def tag(ui, repo, name1, *names, **opts):
4910 """add one or more tags for the current or given revision
4910 """add one or more tags for the current or given revision
4911
4911
4912 Name a particular revision using <name>.
4912 Name a particular revision using <name>.
4913
4913
4914 Tags are used to name particular revisions of the repository and are
4914 Tags are used to name particular revisions of the repository and are
4915 very useful to compare different revisions, to go back to significant
4915 very useful to compare different revisions, to go back to significant
4916 earlier versions or to mark branch points as releases, etc. Changing
4916 earlier versions or to mark branch points as releases, etc. Changing
4917 an existing tag is normally disallowed; use -f/--force to override.
4917 an existing tag is normally disallowed; use -f/--force to override.
4918
4918
4919 If no revision is given, the parent of the working directory is
4919 If no revision is given, the parent of the working directory is
4920 used, or tip if no revision is checked out.
4920 used, or tip if no revision is checked out.
4921
4921
4922 To facilitate version control, distribution, and merging of tags,
4922 To facilitate version control, distribution, and merging of tags,
4923 they are stored as a file named ".hgtags" which is managed similarly
4923 they are stored as a file named ".hgtags" which is managed similarly
4924 to other project files and can be hand-edited if necessary. This
4924 to other project files and can be hand-edited if necessary. This
4925 also means that tagging creates a new commit. The file
4925 also means that tagging creates a new commit. The file
4926 ".hg/localtags" is used for local tags (not shared among
4926 ".hg/localtags" is used for local tags (not shared among
4927 repositories).
4927 repositories).
4928
4928
4929 Tag commits are usually made at the head of a branch. If the parent
4929 Tag commits are usually made at the head of a branch. If the parent
4930 of the working directory is not a branch head, :hg:`tag` aborts; use
4930 of the working directory is not a branch head, :hg:`tag` aborts; use
4931 -f/--force to force the tag commit to be based on a non-head
4931 -f/--force to force the tag commit to be based on a non-head
4932 changeset.
4932 changeset.
4933
4933
4934 See :hg:`help dates` for a list of formats valid for -d/--date.
4934 See :hg:`help dates` for a list of formats valid for -d/--date.
4935
4935
4936 Since tag names have priority over branch names during revision
4936 Since tag names have priority over branch names during revision
4937 lookup, using an existing branch name as a tag name is discouraged.
4937 lookup, using an existing branch name as a tag name is discouraged.
4938
4938
4939 Returns 0 on success.
4939 Returns 0 on success.
4940 """
4940 """
4941
4941
4942 rev_ = "."
4942 rev_ = "."
4943 names = [t.strip() for t in (name1,) + names]
4943 names = [t.strip() for t in (name1,) + names]
4944 if len(names) != len(set(names)):
4944 if len(names) != len(set(names)):
4945 raise util.Abort(_('tag names must be unique'))
4945 raise util.Abort(_('tag names must be unique'))
4946 for n in names:
4946 for n in names:
4947 if n in ['tip', '.', 'null']:
4947 if n in ['tip', '.', 'null']:
4948 raise util.Abort(_("the name '%s' is reserved") % n)
4948 raise util.Abort(_("the name '%s' is reserved") % n)
4949 if not n:
4949 if not n:
4950 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
4950 raise util.Abort(_('tag names cannot consist entirely of whitespace'))
4951 if opts.get('rev') and opts.get('remove'):
4951 if opts.get('rev') and opts.get('remove'):
4952 raise util.Abort(_("--rev and --remove are incompatible"))
4952 raise util.Abort(_("--rev and --remove are incompatible"))
4953 if opts.get('rev'):
4953 if opts.get('rev'):
4954 rev_ = opts['rev']
4954 rev_ = opts['rev']
4955 message = opts.get('message')
4955 message = opts.get('message')
4956 if opts.get('remove'):
4956 if opts.get('remove'):
4957 expectedtype = opts.get('local') and 'local' or 'global'
4957 expectedtype = opts.get('local') and 'local' or 'global'
4958 for n in names:
4958 for n in names:
4959 if not repo.tagtype(n):
4959 if not repo.tagtype(n):
4960 raise util.Abort(_("tag '%s' does not exist") % n)
4960 raise util.Abort(_("tag '%s' does not exist") % n)
4961 if repo.tagtype(n) != expectedtype:
4961 if repo.tagtype(n) != expectedtype:
4962 if expectedtype == 'global':
4962 if expectedtype == 'global':
4963 raise util.Abort(_("tag '%s' is not a global tag") % n)
4963 raise util.Abort(_("tag '%s' is not a global tag") % n)
4964 else:
4964 else:
4965 raise util.Abort(_("tag '%s' is not a local tag") % n)
4965 raise util.Abort(_("tag '%s' is not a local tag") % n)
4966 rev_ = nullid
4966 rev_ = nullid
4967 if not message:
4967 if not message:
4968 # we don't translate commit messages
4968 # we don't translate commit messages
4969 message = 'Removed tag %s' % ', '.join(names)
4969 message = 'Removed tag %s' % ', '.join(names)
4970 elif not opts.get('force'):
4970 elif not opts.get('force'):
4971 for n in names:
4971 for n in names:
4972 if n in repo.tags():
4972 if n in repo.tags():
4973 raise util.Abort(_("tag '%s' already exists "
4973 raise util.Abort(_("tag '%s' already exists "
4974 "(use -f to force)") % n)
4974 "(use -f to force)") % n)
4975 if not opts.get('local'):
4975 if not opts.get('local'):
4976 p1, p2 = repo.dirstate.parents()
4976 p1, p2 = repo.dirstate.parents()
4977 if p2 != nullid:
4977 if p2 != nullid:
4978 raise util.Abort(_('uncommitted merge'))
4978 raise util.Abort(_('uncommitted merge'))
4979 bheads = repo.branchheads()
4979 bheads = repo.branchheads()
4980 if not opts.get('force') and bheads and p1 not in bheads:
4980 if not opts.get('force') and bheads and p1 not in bheads:
4981 raise util.Abort(_('not at a branch head (use -f to force)'))
4981 raise util.Abort(_('not at a branch head (use -f to force)'))
4982 r = scmutil.revsingle(repo, rev_).node()
4982 r = scmutil.revsingle(repo, rev_).node()
4983
4983
4984 if not message:
4984 if not message:
4985 # we don't translate commit messages
4985 # we don't translate commit messages
4986 message = ('Added tag %s for changeset %s' %
4986 message = ('Added tag %s for changeset %s' %
4987 (', '.join(names), short(r)))
4987 (', '.join(names), short(r)))
4988
4988
4989 date = opts.get('date')
4989 date = opts.get('date')
4990 if date:
4990 if date:
4991 date = util.parsedate(date)
4991 date = util.parsedate(date)
4992
4992
4993 if opts.get('edit'):
4993 if opts.get('edit'):
4994 message = ui.edit(message, ui.username())
4994 message = ui.edit(message, ui.username())
4995
4995
4996 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
4996 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
4997
4997
4998 @command('tags', [], '')
4998 @command('tags', [], '')
4999 def tags(ui, repo):
4999 def tags(ui, repo):
5000 """list repository tags
5000 """list repository tags
5001
5001
5002 This lists both regular and local tags. When the -v/--verbose
5002 This lists both regular and local tags. When the -v/--verbose
5003 switch is used, a third column "local" is printed for local tags.
5003 switch is used, a third column "local" is printed for local tags.
5004
5004
5005 Returns 0 on success.
5005 Returns 0 on success.
5006 """
5006 """
5007
5007
5008 hexfunc = ui.debugflag and hex or short
5008 hexfunc = ui.debugflag and hex or short
5009 tagtype = ""
5009 tagtype = ""
5010
5010
5011 for t, n in reversed(repo.tagslist()):
5011 for t, n in reversed(repo.tagslist()):
5012 if ui.quiet:
5012 if ui.quiet:
5013 ui.write("%s\n" % t, label='tags.normal')
5013 ui.write("%s\n" % t, label='tags.normal')
5014 continue
5014 continue
5015
5015
5016 hn = hexfunc(n)
5016 hn = hexfunc(n)
5017 r = "%5d:%s" % (repo.changelog.rev(n), hn)
5017 r = "%5d:%s" % (repo.changelog.rev(n), hn)
5018 rev = ui.label(r, 'log.changeset')
5018 rev = ui.label(r, 'log.changeset')
5019 spaces = " " * (30 - encoding.colwidth(t))
5019 spaces = " " * (30 - encoding.colwidth(t))
5020
5020
5021 tag = ui.label(t, 'tags.normal')
5021 tag = ui.label(t, 'tags.normal')
5022 if ui.verbose:
5022 if ui.verbose:
5023 if repo.tagtype(t) == 'local':
5023 if repo.tagtype(t) == 'local':
5024 tagtype = " local"
5024 tagtype = " local"
5025 tag = ui.label(t, 'tags.local')
5025 tag = ui.label(t, 'tags.local')
5026 else:
5026 else:
5027 tagtype = ""
5027 tagtype = ""
5028 ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
5028 ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
5029
5029
5030 @command('tip',
5030 @command('tip',
5031 [('p', 'patch', None, _('show patch')),
5031 [('p', 'patch', None, _('show patch')),
5032 ('g', 'git', None, _('use git extended diff format')),
5032 ('g', 'git', None, _('use git extended diff format')),
5033 ] + templateopts,
5033 ] + templateopts,
5034 _('[-p] [-g]'))
5034 _('[-p] [-g]'))
5035 def tip(ui, repo, **opts):
5035 def tip(ui, repo, **opts):
5036 """show the tip revision
5036 """show the tip revision
5037
5037
5038 The tip revision (usually just called the tip) is the changeset
5038 The tip revision (usually just called the tip) is the changeset
5039 most recently added to the repository (and therefore the most
5039 most recently added to the repository (and therefore the most
5040 recently changed head).
5040 recently changed head).
5041
5041
5042 If you have just made a commit, that commit will be the tip. If
5042 If you have just made a commit, that commit will be the tip. If
5043 you have just pulled changes from another repository, the tip of
5043 you have just pulled changes from another repository, the tip of
5044 that repository becomes the current tip. The "tip" tag is special
5044 that repository becomes the current tip. The "tip" tag is special
5045 and cannot be renamed or assigned to a different changeset.
5045 and cannot be renamed or assigned to a different changeset.
5046
5046
5047 Returns 0 on success.
5047 Returns 0 on success.
5048 """
5048 """
5049 displayer = cmdutil.show_changeset(ui, repo, opts)
5049 displayer = cmdutil.show_changeset(ui, repo, opts)
5050 displayer.show(repo[len(repo) - 1])
5050 displayer.show(repo[len(repo) - 1])
5051 displayer.close()
5051 displayer.close()
5052
5052
5053 @command('unbundle',
5053 @command('unbundle',
5054 [('u', 'update', None,
5054 [('u', 'update', None,
5055 _('update to new branch head if changesets were unbundled'))],
5055 _('update to new branch head if changesets were unbundled'))],
5056 _('[-u] FILE...'))
5056 _('[-u] FILE...'))
5057 def unbundle(ui, repo, fname1, *fnames, **opts):
5057 def unbundle(ui, repo, fname1, *fnames, **opts):
5058 """apply one or more changegroup files
5058 """apply one or more changegroup files
5059
5059
5060 Apply one or more compressed changegroup files generated by the
5060 Apply one or more compressed changegroup files generated by the
5061 bundle command.
5061 bundle command.
5062
5062
5063 Returns 0 on success, 1 if an update has unresolved files.
5063 Returns 0 on success, 1 if an update has unresolved files.
5064 """
5064 """
5065 fnames = (fname1,) + fnames
5065 fnames = (fname1,) + fnames
5066
5066
5067 lock = repo.lock()
5067 lock = repo.lock()
5068 wc = repo['.']
5068 wc = repo['.']
5069 try:
5069 try:
5070 for fname in fnames:
5070 for fname in fnames:
5071 f = url.open(ui, fname)
5071 f = url.open(ui, fname)
5072 gen = changegroup.readbundle(f, fname)
5072 gen = changegroup.readbundle(f, fname)
5073 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
5073 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
5074 lock=lock)
5074 lock=lock)
5075 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5075 bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
5076 finally:
5076 finally:
5077 lock.release()
5077 lock.release()
5078 return postincoming(ui, repo, modheads, opts.get('update'), None)
5078 return postincoming(ui, repo, modheads, opts.get('update'), None)
5079
5079
5080 @command('^update|up|checkout|co',
5080 @command('^update|up|checkout|co',
5081 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5081 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
5082 ('c', 'check', None,
5082 ('c', 'check', None,
5083 _('update across branches if no uncommitted changes')),
5083 _('update across branches if no uncommitted changes')),
5084 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5084 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5085 ('r', 'rev', '', _('revision'), _('REV'))],
5085 ('r', 'rev', '', _('revision'), _('REV'))],
5086 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5086 _('[-c] [-C] [-d DATE] [[-r] REV]'))
5087 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5087 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
5088 """update working directory (or switch revisions)
5088 """update working directory (or switch revisions)
5089
5089
5090 Update the repository's working directory to the specified
5090 Update the repository's working directory to the specified
5091 changeset. If no changeset is specified, update to the tip of the
5091 changeset. If no changeset is specified, update to the tip of the
5092 current named branch.
5092 current named branch.
5093
5093
5094 If the changeset is not a descendant of the working directory's
5094 If the changeset is not a descendant of the working directory's
5095 parent, the update is aborted. With the -c/--check option, the
5095 parent, the update is aborted. With the -c/--check option, the
5096 working directory is checked for uncommitted changes; if none are
5096 working directory is checked for uncommitted changes; if none are
5097 found, the working directory is updated to the specified
5097 found, the working directory is updated to the specified
5098 changeset.
5098 changeset.
5099
5099
5100 Update sets the working directory's parent revison to the specified
5100 Update sets the working directory's parent revison to the specified
5101 changeset (see :hg:`help parents`).
5101 changeset (see :hg:`help parents`).
5102
5102
5103 The following rules apply when the working directory contains
5103 The following rules apply when the working directory contains
5104 uncommitted changes:
5104 uncommitted changes:
5105
5105
5106 1. If neither -c/--check nor -C/--clean is specified, and if
5106 1. If neither -c/--check nor -C/--clean is specified, and if
5107 the requested changeset is an ancestor or descendant of
5107 the requested changeset is an ancestor or descendant of
5108 the working directory's parent, the uncommitted changes
5108 the working directory's parent, the uncommitted changes
5109 are merged into the requested changeset and the merged
5109 are merged into the requested changeset and the merged
5110 result is left uncommitted. If the requested changeset is
5110 result is left uncommitted. If the requested changeset is
5111 not an ancestor or descendant (that is, it is on another
5111 not an ancestor or descendant (that is, it is on another
5112 branch), the update is aborted and the uncommitted changes
5112 branch), the update is aborted and the uncommitted changes
5113 are preserved.
5113 are preserved.
5114
5114
5115 2. With the -c/--check option, the update is aborted and the
5115 2. With the -c/--check option, the update is aborted and the
5116 uncommitted changes are preserved.
5116 uncommitted changes are preserved.
5117
5117
5118 3. With the -C/--clean option, uncommitted changes are discarded and
5118 3. With the -C/--clean option, uncommitted changes are discarded and
5119 the working directory is updated to the requested changeset.
5119 the working directory is updated to the requested changeset.
5120
5120
5121 Use null as the changeset to remove the working directory (like
5121 Use null as the changeset to remove the working directory (like
5122 :hg:`clone -U`).
5122 :hg:`clone -U`).
5123
5123
5124 If you want to revert just one file to an older revision, use
5124 If you want to revert just one file to an older revision, use
5125 :hg:`revert [-r REV] NAME`.
5125 :hg:`revert [-r REV] NAME`.
5126
5126
5127 See :hg:`help dates` for a list of formats valid for -d/--date.
5127 See :hg:`help dates` for a list of formats valid for -d/--date.
5128
5128
5129 Returns 0 on success, 1 if there are unresolved files.
5129 Returns 0 on success, 1 if there are unresolved files.
5130 """
5130 """
5131 if rev and node:
5131 if rev and node:
5132 raise util.Abort(_("please specify just one revision"))
5132 raise util.Abort(_("please specify just one revision"))
5133
5133
5134 if rev is None or rev == '':
5134 if rev is None or rev == '':
5135 rev = node
5135 rev = node
5136
5136
5137 # if we defined a bookmark, we have to remember the original bookmark name
5137 # if we defined a bookmark, we have to remember the original bookmark name
5138 brev = rev
5138 brev = rev
5139 rev = scmutil.revsingle(repo, rev, rev).rev()
5139 rev = scmutil.revsingle(repo, rev, rev).rev()
5140
5140
5141 if check and clean:
5141 if check and clean:
5142 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5142 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
5143
5143
5144 if check:
5144 if check:
5145 # we could use dirty() but we can ignore merge and branch trivia
5145 # we could use dirty() but we can ignore merge and branch trivia
5146 c = repo[None]
5146 c = repo[None]
5147 if c.modified() or c.added() or c.removed():
5147 if c.modified() or c.added() or c.removed():
5148 raise util.Abort(_("uncommitted local changes"))
5148 raise util.Abort(_("uncommitted local changes"))
5149
5149
5150 if date:
5150 if date:
5151 if rev is not None:
5151 if rev is not None:
5152 raise util.Abort(_("you can't specify a revision and a date"))
5152 raise util.Abort(_("you can't specify a revision and a date"))
5153 rev = cmdutil.finddate(ui, repo, date)
5153 rev = cmdutil.finddate(ui, repo, date)
5154
5154
5155 if clean or check:
5155 if clean or check:
5156 ret = hg.clean(repo, rev)
5156 ret = hg.clean(repo, rev)
5157 else:
5157 else:
5158 ret = hg.update(repo, rev)
5158 ret = hg.update(repo, rev)
5159
5159
5160 if brev in repo._bookmarks:
5160 if brev in repo._bookmarks:
5161 bookmarks.setcurrent(repo, brev)
5161 bookmarks.setcurrent(repo, brev)
5162
5162
5163 return ret
5163 return ret
5164
5164
5165 @command('verify', [])
5165 @command('verify', [])
5166 def verify(ui, repo):
5166 def verify(ui, repo):
5167 """verify the integrity of the repository
5167 """verify the integrity of the repository
5168
5168
5169 Verify the integrity of the current repository.
5169 Verify the integrity of the current repository.
5170
5170
5171 This will perform an extensive check of the repository's
5171 This will perform an extensive check of the repository's
5172 integrity, validating the hashes and checksums of each entry in
5172 integrity, validating the hashes and checksums of each entry in
5173 the changelog, manifest, and tracked files, as well as the
5173 the changelog, manifest, and tracked files, as well as the
5174 integrity of their crosslinks and indices.
5174 integrity of their crosslinks and indices.
5175
5175
5176 Returns 0 on success, 1 if errors are encountered.
5176 Returns 0 on success, 1 if errors are encountered.
5177 """
5177 """
5178 return hg.verify(repo)
5178 return hg.verify(repo)
5179
5179
5180 @command('version', [])
5180 @command('version', [])
5181 def version_(ui):
5181 def version_(ui):
5182 """output version and copyright information"""
5182 """output version and copyright information"""
5183 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5183 ui.write(_("Mercurial Distributed SCM (version %s)\n")
5184 % util.version())
5184 % util.version())
5185 ui.status(_(
5185 ui.status(_(
5186 "(see http://mercurial.selenic.com for more information)\n"
5186 "(see http://mercurial.selenic.com for more information)\n"
5187 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
5187 "\nCopyright (C) 2005-2011 Matt Mackall and others\n"
5188 "This is free software; see the source for copying conditions. "
5188 "This is free software; see the source for copying conditions. "
5189 "There is NO\nwarranty; "
5189 "There is NO\nwarranty; "
5190 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5190 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
5191 ))
5191 ))
5192
5192
5193 norepo = ("clone init version help debugcommands debugcomplete"
5193 norepo = ("clone init version help debugcommands debugcomplete"
5194 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5194 " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
5195 " debugknown debuggetbundle debugbundle")
5195 " debugknown debuggetbundle debugbundle")
5196 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5196 optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
5197 " debugdata debugindex debugindexdot debugrevlog")
5197 " debugdata debugindex debugindexdot debugrevlog")
@@ -1,471 +1,473 b''
1 # url.py - HTTP handling for mercurial
1 # url.py - HTTP handling for mercurial
2 #
2 #
3 # Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
4 # Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 import urllib, urllib2, httplib, os, socket, cStringIO
10 import urllib, urllib2, httplib, os, socket, cStringIO
11 from i18n import _
11 from i18n import _
12 import keepalive, util, sslutil
12 import keepalive, util, sslutil
13 import httpconnection as httpconnectionmod
13 import httpconnection as httpconnectionmod
14
14
15 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
15 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
16 def __init__(self, ui):
16 def __init__(self, ui):
17 urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self)
17 urllib2.HTTPPasswordMgrWithDefaultRealm.__init__(self)
18 self.ui = ui
18 self.ui = ui
19
19
20 def find_user_password(self, realm, authuri):
20 def find_user_password(self, realm, authuri):
21 authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
21 authinfo = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
22 self, realm, authuri)
22 self, realm, authuri)
23 user, passwd = authinfo
23 user, passwd = authinfo
24 if user and passwd:
24 if user and passwd:
25 self._writedebug(user, passwd)
25 self._writedebug(user, passwd)
26 return (user, passwd)
26 return (user, passwd)
27
27
28 if not user or not passwd:
28 if not user or not passwd:
29 res = httpconnectionmod.readauthforuri(self.ui, authuri, user)
29 res = httpconnectionmod.readauthforuri(self.ui, authuri, user)
30 if res:
30 if res:
31 group, auth = res
31 group, auth = res
32 user, passwd = auth.get('username'), auth.get('password')
32 user, passwd = auth.get('username'), auth.get('password')
33 self.ui.debug("using auth.%s.* for authentication\n" % group)
33 self.ui.debug("using auth.%s.* for authentication\n" % group)
34 if not user or not passwd:
34 if not user or not passwd:
35 if not self.ui.interactive():
35 if not self.ui.interactive():
36 raise util.Abort(_('http authorization required'))
36 raise util.Abort(_('http authorization required'))
37
37
38 self.ui.write(_("http authorization required\n"))
38 self.ui.write(_("http authorization required\n"))
39 self.ui.write(_("realm: %s\n") % realm)
39 self.ui.write(_("realm: %s\n") % realm)
40 if user:
40 if user:
41 self.ui.write(_("user: %s\n") % user)
41 self.ui.write(_("user: %s\n") % user)
42 else:
42 else:
43 user = self.ui.prompt(_("user:"), default=None)
43 user = self.ui.prompt(_("user:"), default=None)
44
44
45 if not passwd:
45 if not passwd:
46 passwd = self.ui.getpass()
46 passwd = self.ui.getpass()
47
47
48 self.add_password(realm, authuri, user, passwd)
48 self.add_password(realm, authuri, user, passwd)
49 self._writedebug(user, passwd)
49 self._writedebug(user, passwd)
50 return (user, passwd)
50 return (user, passwd)
51
51
52 def _writedebug(self, user, passwd):
52 def _writedebug(self, user, passwd):
53 msg = _('http auth: user %s, password %s\n')
53 msg = _('http auth: user %s, password %s\n')
54 self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set'))
54 self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set'))
55
55
56 def find_stored_password(self, authuri):
56 def find_stored_password(self, authuri):
57 return urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
57 return urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(
58 self, None, authuri)
58 self, None, authuri)
59
59
60 class proxyhandler(urllib2.ProxyHandler):
60 class proxyhandler(urllib2.ProxyHandler):
61 def __init__(self, ui):
61 def __init__(self, ui):
62 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
62 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
63 # XXX proxyauthinfo = None
63 # XXX proxyauthinfo = None
64
64
65 if proxyurl:
65 if proxyurl:
66 # proxy can be proper url or host[:port]
66 # proxy can be proper url or host[:port]
67 if not (proxyurl.startswith('http:') or
67 if not (proxyurl.startswith('http:') or
68 proxyurl.startswith('https:')):
68 proxyurl.startswith('https:')):
69 proxyurl = 'http://' + proxyurl + '/'
69 proxyurl = 'http://' + proxyurl + '/'
70 proxy = util.url(proxyurl)
70 proxy = util.url(proxyurl)
71 if not proxy.user:
71 if not proxy.user:
72 proxy.user = ui.config("http_proxy", "user")
72 proxy.user = ui.config("http_proxy", "user")
73 proxy.passwd = ui.config("http_proxy", "passwd")
73 proxy.passwd = ui.config("http_proxy", "passwd")
74
74
75 # see if we should use a proxy for this url
75 # see if we should use a proxy for this url
76 no_list = ["localhost", "127.0.0.1"]
76 no_list = ["localhost", "127.0.0.1"]
77 no_list.extend([p.lower() for
77 no_list.extend([p.lower() for
78 p in ui.configlist("http_proxy", "no")])
78 p in ui.configlist("http_proxy", "no")])
79 no_list.extend([p.strip().lower() for
79 no_list.extend([p.strip().lower() for
80 p in os.getenv("no_proxy", '').split(',')
80 p in os.getenv("no_proxy", '').split(',')
81 if p.strip()])
81 if p.strip()])
82 # "http_proxy.always" config is for running tests on localhost
82 # "http_proxy.always" config is for running tests on localhost
83 if ui.configbool("http_proxy", "always"):
83 if ui.configbool("http_proxy", "always"):
84 self.no_list = []
84 self.no_list = []
85 else:
85 else:
86 self.no_list = no_list
86 self.no_list = no_list
87
87
88 proxyurl = str(proxy)
88 proxyurl = str(proxy)
89 proxies = {'http': proxyurl, 'https': proxyurl}
89 proxies = {'http': proxyurl, 'https': proxyurl}
90 ui.debug('proxying through http://%s:%s\n' %
90 ui.debug('proxying through http://%s:%s\n' %
91 (proxy.host, proxy.port))
91 (proxy.host, proxy.port))
92 else:
92 else:
93 proxies = {}
93 proxies = {}
94
94
95 # urllib2 takes proxy values from the environment and those
95 # urllib2 takes proxy values from the environment and those
96 # will take precedence if found, so drop them
96 # will take precedence if found. So, if there's a config entry
97 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
97 # defining a proxy, drop the environment ones
98 try:
98 if ui.config("http_proxy", "host"):
99 if env in os.environ:
99 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
100 del os.environ[env]
100 try:
101 except OSError:
101 if env in os.environ:
102 pass
102 del os.environ[env]
103 except OSError:
104 pass
103
105
104 urllib2.ProxyHandler.__init__(self, proxies)
106 urllib2.ProxyHandler.__init__(self, proxies)
105 self.ui = ui
107 self.ui = ui
106
108
107 def proxy_open(self, req, proxy, type_):
109 def proxy_open(self, req, proxy, type_):
108 host = req.get_host().split(':')[0]
110 host = req.get_host().split(':')[0]
109 if host in self.no_list:
111 if host in self.no_list:
110 return None
112 return None
111
113
112 # work around a bug in Python < 2.4.2
114 # work around a bug in Python < 2.4.2
113 # (it leaves a "\n" at the end of Proxy-authorization headers)
115 # (it leaves a "\n" at the end of Proxy-authorization headers)
114 baseclass = req.__class__
116 baseclass = req.__class__
115 class _request(baseclass):
117 class _request(baseclass):
116 def add_header(self, key, val):
118 def add_header(self, key, val):
117 if key.lower() == 'proxy-authorization':
119 if key.lower() == 'proxy-authorization':
118 val = val.strip()
120 val = val.strip()
119 return baseclass.add_header(self, key, val)
121 return baseclass.add_header(self, key, val)
120 req.__class__ = _request
122 req.__class__ = _request
121
123
122 return urllib2.ProxyHandler.proxy_open(self, req, proxy, type_)
124 return urllib2.ProxyHandler.proxy_open(self, req, proxy, type_)
123
125
124 def _gen_sendfile(orgsend):
126 def _gen_sendfile(orgsend):
125 def _sendfile(self, data):
127 def _sendfile(self, data):
126 # send a file
128 # send a file
127 if isinstance(data, httpconnectionmod.httpsendfile):
129 if isinstance(data, httpconnectionmod.httpsendfile):
128 # if auth required, some data sent twice, so rewind here
130 # if auth required, some data sent twice, so rewind here
129 data.seek(0)
131 data.seek(0)
130 for chunk in util.filechunkiter(data):
132 for chunk in util.filechunkiter(data):
131 orgsend(self, chunk)
133 orgsend(self, chunk)
132 else:
134 else:
133 orgsend(self, data)
135 orgsend(self, data)
134 return _sendfile
136 return _sendfile
135
137
136 has_https = util.safehasattr(urllib2, 'HTTPSHandler')
138 has_https = util.safehasattr(urllib2, 'HTTPSHandler')
137 if has_https:
139 if has_https:
138 try:
140 try:
139 _create_connection = socket.create_connection
141 _create_connection = socket.create_connection
140 except AttributeError:
142 except AttributeError:
141 _GLOBAL_DEFAULT_TIMEOUT = object()
143 _GLOBAL_DEFAULT_TIMEOUT = object()
142
144
143 def _create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
145 def _create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
144 source_address=None):
146 source_address=None):
145 # lifted from Python 2.6
147 # lifted from Python 2.6
146
148
147 msg = "getaddrinfo returns an empty list"
149 msg = "getaddrinfo returns an empty list"
148 host, port = address
150 host, port = address
149 for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
151 for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
150 af, socktype, proto, canonname, sa = res
152 af, socktype, proto, canonname, sa = res
151 sock = None
153 sock = None
152 try:
154 try:
153 sock = socket.socket(af, socktype, proto)
155 sock = socket.socket(af, socktype, proto)
154 if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
156 if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
155 sock.settimeout(timeout)
157 sock.settimeout(timeout)
156 if source_address:
158 if source_address:
157 sock.bind(source_address)
159 sock.bind(source_address)
158 sock.connect(sa)
160 sock.connect(sa)
159 return sock
161 return sock
160
162
161 except socket.error, msg:
163 except socket.error, msg:
162 if sock is not None:
164 if sock is not None:
163 sock.close()
165 sock.close()
164
166
165 raise socket.error, msg
167 raise socket.error, msg
166
168
167 class httpconnection(keepalive.HTTPConnection):
169 class httpconnection(keepalive.HTTPConnection):
168 # must be able to send big bundle as stream.
170 # must be able to send big bundle as stream.
169 send = _gen_sendfile(keepalive.HTTPConnection.send)
171 send = _gen_sendfile(keepalive.HTTPConnection.send)
170
172
171 def connect(self):
173 def connect(self):
172 if has_https and self.realhostport: # use CONNECT proxy
174 if has_https and self.realhostport: # use CONNECT proxy
173 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
175 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
174 self.sock.connect((self.host, self.port))
176 self.sock.connect((self.host, self.port))
175 if _generic_proxytunnel(self):
177 if _generic_proxytunnel(self):
176 # we do not support client x509 certificates
178 # we do not support client x509 certificates
177 self.sock = sslutil.ssl_wrap_socket(self.sock, None, None)
179 self.sock = sslutil.ssl_wrap_socket(self.sock, None, None)
178 else:
180 else:
179 keepalive.HTTPConnection.connect(self)
181 keepalive.HTTPConnection.connect(self)
180
182
181 def getresponse(self):
183 def getresponse(self):
182 proxyres = getattr(self, 'proxyres', None)
184 proxyres = getattr(self, 'proxyres', None)
183 if proxyres:
185 if proxyres:
184 if proxyres.will_close:
186 if proxyres.will_close:
185 self.close()
187 self.close()
186 self.proxyres = None
188 self.proxyres = None
187 return proxyres
189 return proxyres
188 return keepalive.HTTPConnection.getresponse(self)
190 return keepalive.HTTPConnection.getresponse(self)
189
191
190 # general transaction handler to support different ways to handle
192 # general transaction handler to support different ways to handle
191 # HTTPS proxying before and after Python 2.6.3.
193 # HTTPS proxying before and after Python 2.6.3.
192 def _generic_start_transaction(handler, h, req):
194 def _generic_start_transaction(handler, h, req):
193 tunnel_host = getattr(req, '_tunnel_host', None)
195 tunnel_host = getattr(req, '_tunnel_host', None)
194 if tunnel_host:
196 if tunnel_host:
195 if tunnel_host[:7] not in ['http://', 'https:/']:
197 if tunnel_host[:7] not in ['http://', 'https:/']:
196 tunnel_host = 'https://' + tunnel_host
198 tunnel_host = 'https://' + tunnel_host
197 new_tunnel = True
199 new_tunnel = True
198 else:
200 else:
199 tunnel_host = req.get_selector()
201 tunnel_host = req.get_selector()
200 new_tunnel = False
202 new_tunnel = False
201
203
202 if new_tunnel or tunnel_host == req.get_full_url(): # has proxy
204 if new_tunnel or tunnel_host == req.get_full_url(): # has proxy
203 u = util.url(tunnel_host)
205 u = util.url(tunnel_host)
204 if new_tunnel or u.scheme == 'https': # only use CONNECT for HTTPS
206 if new_tunnel or u.scheme == 'https': # only use CONNECT for HTTPS
205 h.realhostport = ':'.join([u.host, (u.port or '443')])
207 h.realhostport = ':'.join([u.host, (u.port or '443')])
206 h.headers = req.headers.copy()
208 h.headers = req.headers.copy()
207 h.headers.update(handler.parent.addheaders)
209 h.headers.update(handler.parent.addheaders)
208 return
210 return
209
211
210 h.realhostport = None
212 h.realhostport = None
211 h.headers = None
213 h.headers = None
212
214
213 def _generic_proxytunnel(self):
215 def _generic_proxytunnel(self):
214 proxyheaders = dict(
216 proxyheaders = dict(
215 [(x, self.headers[x]) for x in self.headers
217 [(x, self.headers[x]) for x in self.headers
216 if x.lower().startswith('proxy-')])
218 if x.lower().startswith('proxy-')])
217 self._set_hostport(self.host, self.port)
219 self._set_hostport(self.host, self.port)
218 self.send('CONNECT %s HTTP/1.0\r\n' % self.realhostport)
220 self.send('CONNECT %s HTTP/1.0\r\n' % self.realhostport)
219 for header in proxyheaders.iteritems():
221 for header in proxyheaders.iteritems():
220 self.send('%s: %s\r\n' % header)
222 self.send('%s: %s\r\n' % header)
221 self.send('\r\n')
223 self.send('\r\n')
222
224
223 # majority of the following code is duplicated from
225 # majority of the following code is duplicated from
224 # httplib.HTTPConnection as there are no adequate places to
226 # httplib.HTTPConnection as there are no adequate places to
225 # override functions to provide the needed functionality
227 # override functions to provide the needed functionality
226 res = self.response_class(self.sock,
228 res = self.response_class(self.sock,
227 strict=self.strict,
229 strict=self.strict,
228 method=self._method)
230 method=self._method)
229
231
230 while True:
232 while True:
231 version, status, reason = res._read_status()
233 version, status, reason = res._read_status()
232 if status != httplib.CONTINUE:
234 if status != httplib.CONTINUE:
233 break
235 break
234 while True:
236 while True:
235 skip = res.fp.readline().strip()
237 skip = res.fp.readline().strip()
236 if not skip:
238 if not skip:
237 break
239 break
238 res.status = status
240 res.status = status
239 res.reason = reason.strip()
241 res.reason = reason.strip()
240
242
241 if res.status == 200:
243 if res.status == 200:
242 while True:
244 while True:
243 line = res.fp.readline()
245 line = res.fp.readline()
244 if line == '\r\n':
246 if line == '\r\n':
245 break
247 break
246 return True
248 return True
247
249
248 if version == 'HTTP/1.0':
250 if version == 'HTTP/1.0':
249 res.version = 10
251 res.version = 10
250 elif version.startswith('HTTP/1.'):
252 elif version.startswith('HTTP/1.'):
251 res.version = 11
253 res.version = 11
252 elif version == 'HTTP/0.9':
254 elif version == 'HTTP/0.9':
253 res.version = 9
255 res.version = 9
254 else:
256 else:
255 raise httplib.UnknownProtocol(version)
257 raise httplib.UnknownProtocol(version)
256
258
257 if res.version == 9:
259 if res.version == 9:
258 res.length = None
260 res.length = None
259 res.chunked = 0
261 res.chunked = 0
260 res.will_close = 1
262 res.will_close = 1
261 res.msg = httplib.HTTPMessage(cStringIO.StringIO())
263 res.msg = httplib.HTTPMessage(cStringIO.StringIO())
262 return False
264 return False
263
265
264 res.msg = httplib.HTTPMessage(res.fp)
266 res.msg = httplib.HTTPMessage(res.fp)
265 res.msg.fp = None
267 res.msg.fp = None
266
268
267 # are we using the chunked-style of transfer encoding?
269 # are we using the chunked-style of transfer encoding?
268 trenc = res.msg.getheader('transfer-encoding')
270 trenc = res.msg.getheader('transfer-encoding')
269 if trenc and trenc.lower() == "chunked":
271 if trenc and trenc.lower() == "chunked":
270 res.chunked = 1
272 res.chunked = 1
271 res.chunk_left = None
273 res.chunk_left = None
272 else:
274 else:
273 res.chunked = 0
275 res.chunked = 0
274
276
275 # will the connection close at the end of the response?
277 # will the connection close at the end of the response?
276 res.will_close = res._check_close()
278 res.will_close = res._check_close()
277
279
278 # do we have a Content-Length?
280 # do we have a Content-Length?
279 # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked"
281 # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked"
280 length = res.msg.getheader('content-length')
282 length = res.msg.getheader('content-length')
281 if length and not res.chunked:
283 if length and not res.chunked:
282 try:
284 try:
283 res.length = int(length)
285 res.length = int(length)
284 except ValueError:
286 except ValueError:
285 res.length = None
287 res.length = None
286 else:
288 else:
287 if res.length < 0: # ignore nonsensical negative lengths
289 if res.length < 0: # ignore nonsensical negative lengths
288 res.length = None
290 res.length = None
289 else:
291 else:
290 res.length = None
292 res.length = None
291
293
292 # does the body have a fixed length? (of zero)
294 # does the body have a fixed length? (of zero)
293 if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or
295 if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or
294 100 <= status < 200 or # 1xx codes
296 100 <= status < 200 or # 1xx codes
295 res._method == 'HEAD'):
297 res._method == 'HEAD'):
296 res.length = 0
298 res.length = 0
297
299
298 # if the connection remains open, and we aren't using chunked, and
300 # if the connection remains open, and we aren't using chunked, and
299 # a content-length was not provided, then assume that the connection
301 # a content-length was not provided, then assume that the connection
300 # WILL close.
302 # WILL close.
301 if (not res.will_close and
303 if (not res.will_close and
302 not res.chunked and
304 not res.chunked and
303 res.length is None):
305 res.length is None):
304 res.will_close = 1
306 res.will_close = 1
305
307
306 self.proxyres = res
308 self.proxyres = res
307
309
308 return False
310 return False
309
311
310 class httphandler(keepalive.HTTPHandler):
312 class httphandler(keepalive.HTTPHandler):
311 def http_open(self, req):
313 def http_open(self, req):
312 return self.do_open(httpconnection, req)
314 return self.do_open(httpconnection, req)
313
315
314 def _start_transaction(self, h, req):
316 def _start_transaction(self, h, req):
315 _generic_start_transaction(self, h, req)
317 _generic_start_transaction(self, h, req)
316 return keepalive.HTTPHandler._start_transaction(self, h, req)
318 return keepalive.HTTPHandler._start_transaction(self, h, req)
317
319
318 if has_https:
320 if has_https:
319 class httpsconnection(httplib.HTTPSConnection):
321 class httpsconnection(httplib.HTTPSConnection):
320 response_class = keepalive.HTTPResponse
322 response_class = keepalive.HTTPResponse
321 # must be able to send big bundle as stream.
323 # must be able to send big bundle as stream.
322 send = _gen_sendfile(keepalive.safesend)
324 send = _gen_sendfile(keepalive.safesend)
323 getresponse = keepalive.wrapgetresponse(httplib.HTTPSConnection)
325 getresponse = keepalive.wrapgetresponse(httplib.HTTPSConnection)
324
326
325 def connect(self):
327 def connect(self):
326 self.sock = _create_connection((self.host, self.port))
328 self.sock = _create_connection((self.host, self.port))
327
329
328 host = self.host
330 host = self.host
329 if self.realhostport: # use CONNECT proxy
331 if self.realhostport: # use CONNECT proxy
330 _generic_proxytunnel(self)
332 _generic_proxytunnel(self)
331 host = self.realhostport.rsplit(':', 1)[0]
333 host = self.realhostport.rsplit(':', 1)[0]
332 self.sock = sslutil.ssl_wrap_socket(
334 self.sock = sslutil.ssl_wrap_socket(
333 self.sock, self.key_file, self.cert_file,
335 self.sock, self.key_file, self.cert_file,
334 **sslutil.sslkwargs(self.ui, host))
336 **sslutil.sslkwargs(self.ui, host))
335 sslutil.validator(self.ui, host)(self.sock)
337 sslutil.validator(self.ui, host)(self.sock)
336
338
337 class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler):
339 class httpshandler(keepalive.KeepAliveHandler, urllib2.HTTPSHandler):
338 def __init__(self, ui):
340 def __init__(self, ui):
339 keepalive.KeepAliveHandler.__init__(self)
341 keepalive.KeepAliveHandler.__init__(self)
340 urllib2.HTTPSHandler.__init__(self)
342 urllib2.HTTPSHandler.__init__(self)
341 self.ui = ui
343 self.ui = ui
342 self.pwmgr = passwordmgr(self.ui)
344 self.pwmgr = passwordmgr(self.ui)
343
345
344 def _start_transaction(self, h, req):
346 def _start_transaction(self, h, req):
345 _generic_start_transaction(self, h, req)
347 _generic_start_transaction(self, h, req)
346 return keepalive.KeepAliveHandler._start_transaction(self, h, req)
348 return keepalive.KeepAliveHandler._start_transaction(self, h, req)
347
349
348 def https_open(self, req):
350 def https_open(self, req):
349 # req.get_full_url() does not contain credentials and we may
351 # req.get_full_url() does not contain credentials and we may
350 # need them to match the certificates.
352 # need them to match the certificates.
351 url = req.get_full_url()
353 url = req.get_full_url()
352 user, password = self.pwmgr.find_stored_password(url)
354 user, password = self.pwmgr.find_stored_password(url)
353 res = httpconnectionmod.readauthforuri(self.ui, url, user)
355 res = httpconnectionmod.readauthforuri(self.ui, url, user)
354 if res:
356 if res:
355 group, auth = res
357 group, auth = res
356 self.auth = auth
358 self.auth = auth
357 self.ui.debug("using auth.%s.* for authentication\n" % group)
359 self.ui.debug("using auth.%s.* for authentication\n" % group)
358 else:
360 else:
359 self.auth = None
361 self.auth = None
360 return self.do_open(self._makeconnection, req)
362 return self.do_open(self._makeconnection, req)
361
363
362 def _makeconnection(self, host, port=None, *args, **kwargs):
364 def _makeconnection(self, host, port=None, *args, **kwargs):
363 keyfile = None
365 keyfile = None
364 certfile = None
366 certfile = None
365
367
366 if len(args) >= 1: # key_file
368 if len(args) >= 1: # key_file
367 keyfile = args[0]
369 keyfile = args[0]
368 if len(args) >= 2: # cert_file
370 if len(args) >= 2: # cert_file
369 certfile = args[1]
371 certfile = args[1]
370 args = args[2:]
372 args = args[2:]
371
373
372 # if the user has specified different key/cert files in
374 # if the user has specified different key/cert files in
373 # hgrc, we prefer these
375 # hgrc, we prefer these
374 if self.auth and 'key' in self.auth and 'cert' in self.auth:
376 if self.auth and 'key' in self.auth and 'cert' in self.auth:
375 keyfile = self.auth['key']
377 keyfile = self.auth['key']
376 certfile = self.auth['cert']
378 certfile = self.auth['cert']
377
379
378 conn = httpsconnection(host, port, keyfile, certfile, *args, **kwargs)
380 conn = httpsconnection(host, port, keyfile, certfile, *args, **kwargs)
379 conn.ui = self.ui
381 conn.ui = self.ui
380 return conn
382 return conn
381
383
382 class httpdigestauthhandler(urllib2.HTTPDigestAuthHandler):
384 class httpdigestauthhandler(urllib2.HTTPDigestAuthHandler):
383 def __init__(self, *args, **kwargs):
385 def __init__(self, *args, **kwargs):
384 urllib2.HTTPDigestAuthHandler.__init__(self, *args, **kwargs)
386 urllib2.HTTPDigestAuthHandler.__init__(self, *args, **kwargs)
385 self.retried_req = None
387 self.retried_req = None
386
388
387 def reset_retry_count(self):
389 def reset_retry_count(self):
388 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
390 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
389 # forever. We disable reset_retry_count completely and reset in
391 # forever. We disable reset_retry_count completely and reset in
390 # http_error_auth_reqed instead.
392 # http_error_auth_reqed instead.
391 pass
393 pass
392
394
393 def http_error_auth_reqed(self, auth_header, host, req, headers):
395 def http_error_auth_reqed(self, auth_header, host, req, headers):
394 # Reset the retry counter once for each request.
396 # Reset the retry counter once for each request.
395 if req is not self.retried_req:
397 if req is not self.retried_req:
396 self.retried_req = req
398 self.retried_req = req
397 self.retried = 0
399 self.retried = 0
398 # In python < 2.5 AbstractDigestAuthHandler raises a ValueError if
400 # In python < 2.5 AbstractDigestAuthHandler raises a ValueError if
399 # it doesn't know about the auth type requested. This can happen if
401 # it doesn't know about the auth type requested. This can happen if
400 # somebody is using BasicAuth and types a bad password.
402 # somebody is using BasicAuth and types a bad password.
401 try:
403 try:
402 return urllib2.HTTPDigestAuthHandler.http_error_auth_reqed(
404 return urllib2.HTTPDigestAuthHandler.http_error_auth_reqed(
403 self, auth_header, host, req, headers)
405 self, auth_header, host, req, headers)
404 except ValueError, inst:
406 except ValueError, inst:
405 arg = inst.args[0]
407 arg = inst.args[0]
406 if arg.startswith("AbstractDigestAuthHandler doesn't know "):
408 if arg.startswith("AbstractDigestAuthHandler doesn't know "):
407 return
409 return
408 raise
410 raise
409
411
410 class httpbasicauthhandler(urllib2.HTTPBasicAuthHandler):
412 class httpbasicauthhandler(urllib2.HTTPBasicAuthHandler):
411 def __init__(self, *args, **kwargs):
413 def __init__(self, *args, **kwargs):
412 urllib2.HTTPBasicAuthHandler.__init__(self, *args, **kwargs)
414 urllib2.HTTPBasicAuthHandler.__init__(self, *args, **kwargs)
413 self.retried_req = None
415 self.retried_req = None
414
416
415 def reset_retry_count(self):
417 def reset_retry_count(self):
416 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
418 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
417 # forever. We disable reset_retry_count completely and reset in
419 # forever. We disable reset_retry_count completely and reset in
418 # http_error_auth_reqed instead.
420 # http_error_auth_reqed instead.
419 pass
421 pass
420
422
421 def http_error_auth_reqed(self, auth_header, host, req, headers):
423 def http_error_auth_reqed(self, auth_header, host, req, headers):
422 # Reset the retry counter once for each request.
424 # Reset the retry counter once for each request.
423 if req is not self.retried_req:
425 if req is not self.retried_req:
424 self.retried_req = req
426 self.retried_req = req
425 self.retried = 0
427 self.retried = 0
426 return urllib2.HTTPBasicAuthHandler.http_error_auth_reqed(
428 return urllib2.HTTPBasicAuthHandler.http_error_auth_reqed(
427 self, auth_header, host, req, headers)
429 self, auth_header, host, req, headers)
428
430
429 handlerfuncs = []
431 handlerfuncs = []
430
432
431 def opener(ui, authinfo=None):
433 def opener(ui, authinfo=None):
432 '''
434 '''
433 construct an opener suitable for urllib2
435 construct an opener suitable for urllib2
434 authinfo will be added to the password manager
436 authinfo will be added to the password manager
435 '''
437 '''
436 if ui.configbool('ui', 'usehttp2', False):
438 if ui.configbool('ui', 'usehttp2', False):
437 handlers = [httpconnectionmod.http2handler(ui, passwordmgr(ui))]
439 handlers = [httpconnectionmod.http2handler(ui, passwordmgr(ui))]
438 else:
440 else:
439 handlers = [httphandler()]
441 handlers = [httphandler()]
440 if has_https:
442 if has_https:
441 handlers.append(httpshandler(ui))
443 handlers.append(httpshandler(ui))
442
444
443 handlers.append(proxyhandler(ui))
445 handlers.append(proxyhandler(ui))
444
446
445 passmgr = passwordmgr(ui)
447 passmgr = passwordmgr(ui)
446 if authinfo is not None:
448 if authinfo is not None:
447 passmgr.add_password(*authinfo)
449 passmgr.add_password(*authinfo)
448 user, passwd = authinfo[2:4]
450 user, passwd = authinfo[2:4]
449 ui.debug('http auth: user %s, password %s\n' %
451 ui.debug('http auth: user %s, password %s\n' %
450 (user, passwd and '*' * len(passwd) or 'not set'))
452 (user, passwd and '*' * len(passwd) or 'not set'))
451
453
452 handlers.extend((httpbasicauthhandler(passmgr),
454 handlers.extend((httpbasicauthhandler(passmgr),
453 httpdigestauthhandler(passmgr)))
455 httpdigestauthhandler(passmgr)))
454 handlers.extend([h(ui, passmgr) for h in handlerfuncs])
456 handlers.extend([h(ui, passmgr) for h in handlerfuncs])
455 opener = urllib2.build_opener(*handlers)
457 opener = urllib2.build_opener(*handlers)
456
458
457 # 1.0 here is the _protocol_ version
459 # 1.0 here is the _protocol_ version
458 opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
460 opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
459 opener.addheaders.append(('Accept', 'application/mercurial-0.1'))
461 opener.addheaders.append(('Accept', 'application/mercurial-0.1'))
460 return opener
462 return opener
461
463
462 def open(ui, url_, data=None):
464 def open(ui, url_, data=None):
463 u = util.url(url_)
465 u = util.url(url_)
464 if u.scheme:
466 if u.scheme:
465 u.scheme = u.scheme.lower()
467 u.scheme = u.scheme.lower()
466 url_, authinfo = u.authinfo()
468 url_, authinfo = u.authinfo()
467 else:
469 else:
468 path = util.normpath(os.path.abspath(url_))
470 path = util.normpath(os.path.abspath(url_))
469 url_ = 'file://' + urllib.pathname2url(path)
471 url_ = 'file://' + urllib.pathname2url(path)
470 authinfo = None
472 authinfo = None
471 return opener(ui, authinfo).open(url_, data)
473 return opener(ui, authinfo).open(url_, data)
@@ -1,1721 +1,1724 b''
1 # util.py - Mercurial utility functions and platform specfic implementations
1 # util.py - Mercurial utility functions and platform specfic implementations
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 """Mercurial utility functions and platform specfic implementations.
10 """Mercurial utility functions and platform specfic implementations.
11
11
12 This contains helper routines that are independent of the SCM core and
12 This contains helper routines that are independent of the SCM core and
13 hide platform-specific details from the core.
13 hide platform-specific details from the core.
14 """
14 """
15
15
16 from i18n import _
16 from i18n import _
17 import error, osutil, encoding
17 import error, osutil, encoding
18 import errno, re, shutil, sys, tempfile, traceback
18 import errno, re, shutil, sys, tempfile, traceback
19 import os, time, calendar, textwrap, signal
19 import os, time, calendar, textwrap, signal
20 import imp, socket, urllib
20 import imp, socket, urllib
21
21
22 if os.name == 'nt':
22 if os.name == 'nt':
23 import windows as platform
23 import windows as platform
24 else:
24 else:
25 import posix as platform
25 import posix as platform
26
26
27 cachestat = platform.cachestat
27 cachestat = platform.cachestat
28 checkexec = platform.checkexec
28 checkexec = platform.checkexec
29 checklink = platform.checklink
29 checklink = platform.checklink
30 copymode = platform.copymode
30 copymode = platform.copymode
31 executablepath = platform.executablepath
31 executablepath = platform.executablepath
32 expandglobs = platform.expandglobs
32 expandglobs = platform.expandglobs
33 explainexit = platform.explainexit
33 explainexit = platform.explainexit
34 findexe = platform.findexe
34 findexe = platform.findexe
35 gethgcmd = platform.gethgcmd
35 gethgcmd = platform.gethgcmd
36 getuser = platform.getuser
36 getuser = platform.getuser
37 groupmembers = platform.groupmembers
37 groupmembers = platform.groupmembers
38 groupname = platform.groupname
38 groupname = platform.groupname
39 hidewindow = platform.hidewindow
39 hidewindow = platform.hidewindow
40 isexec = platform.isexec
40 isexec = platform.isexec
41 isowner = platform.isowner
41 isowner = platform.isowner
42 localpath = platform.localpath
42 localpath = platform.localpath
43 lookupreg = platform.lookupreg
43 lookupreg = platform.lookupreg
44 makedir = platform.makedir
44 makedir = platform.makedir
45 nlinks = platform.nlinks
45 nlinks = platform.nlinks
46 normpath = platform.normpath
46 normpath = platform.normpath
47 nulldev = platform.nulldev
47 nulldev = platform.nulldev
48 openhardlinks = platform.openhardlinks
48 openhardlinks = platform.openhardlinks
49 oslink = platform.oslink
49 oslink = platform.oslink
50 parsepatchoutput = platform.parsepatchoutput
50 parsepatchoutput = platform.parsepatchoutput
51 pconvert = platform.pconvert
51 pconvert = platform.pconvert
52 popen = platform.popen
52 popen = platform.popen
53 posixfile = platform.posixfile
53 posixfile = platform.posixfile
54 quotecommand = platform.quotecommand
54 quotecommand = platform.quotecommand
55 realpath = platform.realpath
55 realpath = platform.realpath
56 rename = platform.rename
56 rename = platform.rename
57 samedevice = platform.samedevice
57 samedevice = platform.samedevice
58 samefile = platform.samefile
58 samefile = platform.samefile
59 samestat = platform.samestat
59 samestat = platform.samestat
60 setbinary = platform.setbinary
60 setbinary = platform.setbinary
61 setflags = platform.setflags
61 setflags = platform.setflags
62 setsignalhandler = platform.setsignalhandler
62 setsignalhandler = platform.setsignalhandler
63 shellquote = platform.shellquote
63 shellquote = platform.shellquote
64 spawndetached = platform.spawndetached
64 spawndetached = platform.spawndetached
65 sshargs = platform.sshargs
65 sshargs = platform.sshargs
66 statfiles = platform.statfiles
66 statfiles = platform.statfiles
67 termwidth = platform.termwidth
67 termwidth = platform.termwidth
68 testpid = platform.testpid
68 testpid = platform.testpid
69 umask = platform.umask
69 umask = platform.umask
70 unlink = platform.unlink
70 unlink = platform.unlink
71 unlinkpath = platform.unlinkpath
71 unlinkpath = platform.unlinkpath
72 username = platform.username
72 username = platform.username
73
73
74 # Python compatibility
74 # Python compatibility
75
75
76 def sha1(s):
76 def sha1(s):
77 return _fastsha1(s)
77 return _fastsha1(s)
78
78
79 _notset = object()
79 _notset = object()
80 def safehasattr(thing, attr):
80 def safehasattr(thing, attr):
81 return getattr(thing, attr, _notset) is not _notset
81 return getattr(thing, attr, _notset) is not _notset
82
82
83 def _fastsha1(s):
83 def _fastsha1(s):
84 # This function will import sha1 from hashlib or sha (whichever is
84 # This function will import sha1 from hashlib or sha (whichever is
85 # available) and overwrite itself with it on the first call.
85 # available) and overwrite itself with it on the first call.
86 # Subsequent calls will go directly to the imported function.
86 # Subsequent calls will go directly to the imported function.
87 if sys.version_info >= (2, 5):
87 if sys.version_info >= (2, 5):
88 from hashlib import sha1 as _sha1
88 from hashlib import sha1 as _sha1
89 else:
89 else:
90 from sha import sha as _sha1
90 from sha import sha as _sha1
91 global _fastsha1, sha1
91 global _fastsha1, sha1
92 _fastsha1 = sha1 = _sha1
92 _fastsha1 = sha1 = _sha1
93 return _sha1(s)
93 return _sha1(s)
94
94
95 import __builtin__
95 import __builtin__
96
96
97 if sys.version_info[0] < 3:
97 if sys.version_info[0] < 3:
98 def fakebuffer(sliceable, offset=0):
98 def fakebuffer(sliceable, offset=0):
99 return sliceable[offset:]
99 return sliceable[offset:]
100 else:
100 else:
101 def fakebuffer(sliceable, offset=0):
101 def fakebuffer(sliceable, offset=0):
102 return memoryview(sliceable)[offset:]
102 return memoryview(sliceable)[offset:]
103 try:
103 try:
104 buffer
104 buffer
105 except NameError:
105 except NameError:
106 __builtin__.buffer = fakebuffer
106 __builtin__.buffer = fakebuffer
107
107
108 import subprocess
108 import subprocess
109 closefds = os.name == 'posix'
109 closefds = os.name == 'posix'
110
110
111 def popen2(cmd, env=None, newlines=False):
111 def popen2(cmd, env=None, newlines=False):
112 # Setting bufsize to -1 lets the system decide the buffer size.
112 # Setting bufsize to -1 lets the system decide the buffer size.
113 # The default for bufsize is 0, meaning unbuffered. This leads to
113 # The default for bufsize is 0, meaning unbuffered. This leads to
114 # poor performance on Mac OS X: http://bugs.python.org/issue4194
114 # poor performance on Mac OS X: http://bugs.python.org/issue4194
115 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
115 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
116 close_fds=closefds,
116 close_fds=closefds,
117 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
117 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
118 universal_newlines=newlines,
118 universal_newlines=newlines,
119 env=env)
119 env=env)
120 return p.stdin, p.stdout
120 return p.stdin, p.stdout
121
121
122 def popen3(cmd, env=None, newlines=False):
122 def popen3(cmd, env=None, newlines=False):
123 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
123 p = subprocess.Popen(cmd, shell=True, bufsize=-1,
124 close_fds=closefds,
124 close_fds=closefds,
125 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
125 stdin=subprocess.PIPE, stdout=subprocess.PIPE,
126 stderr=subprocess.PIPE,
126 stderr=subprocess.PIPE,
127 universal_newlines=newlines,
127 universal_newlines=newlines,
128 env=env)
128 env=env)
129 return p.stdin, p.stdout, p.stderr
129 return p.stdin, p.stdout, p.stderr
130
130
131 def version():
131 def version():
132 """Return version information if available."""
132 """Return version information if available."""
133 try:
133 try:
134 import __version__
134 import __version__
135 return __version__.version
135 return __version__.version
136 except ImportError:
136 except ImportError:
137 return 'unknown'
137 return 'unknown'
138
138
139 # used by parsedate
139 # used by parsedate
140 defaultdateformats = (
140 defaultdateformats = (
141 '%Y-%m-%d %H:%M:%S',
141 '%Y-%m-%d %H:%M:%S',
142 '%Y-%m-%d %I:%M:%S%p',
142 '%Y-%m-%d %I:%M:%S%p',
143 '%Y-%m-%d %H:%M',
143 '%Y-%m-%d %H:%M',
144 '%Y-%m-%d %I:%M%p',
144 '%Y-%m-%d %I:%M%p',
145 '%Y-%m-%d',
145 '%Y-%m-%d',
146 '%m-%d',
146 '%m-%d',
147 '%m/%d',
147 '%m/%d',
148 '%m/%d/%y',
148 '%m/%d/%y',
149 '%m/%d/%Y',
149 '%m/%d/%Y',
150 '%a %b %d %H:%M:%S %Y',
150 '%a %b %d %H:%M:%S %Y',
151 '%a %b %d %I:%M:%S%p %Y',
151 '%a %b %d %I:%M:%S%p %Y',
152 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
152 '%a, %d %b %Y %H:%M:%S', # GNU coreutils "/bin/date --rfc-2822"
153 '%b %d %H:%M:%S %Y',
153 '%b %d %H:%M:%S %Y',
154 '%b %d %I:%M:%S%p %Y',
154 '%b %d %I:%M:%S%p %Y',
155 '%b %d %H:%M:%S',
155 '%b %d %H:%M:%S',
156 '%b %d %I:%M:%S%p',
156 '%b %d %I:%M:%S%p',
157 '%b %d %H:%M',
157 '%b %d %H:%M',
158 '%b %d %I:%M%p',
158 '%b %d %I:%M%p',
159 '%b %d %Y',
159 '%b %d %Y',
160 '%b %d',
160 '%b %d',
161 '%H:%M:%S',
161 '%H:%M:%S',
162 '%I:%M:%S%p',
162 '%I:%M:%S%p',
163 '%H:%M',
163 '%H:%M',
164 '%I:%M%p',
164 '%I:%M%p',
165 )
165 )
166
166
167 extendeddateformats = defaultdateformats + (
167 extendeddateformats = defaultdateformats + (
168 "%Y",
168 "%Y",
169 "%Y-%m",
169 "%Y-%m",
170 "%b",
170 "%b",
171 "%b %Y",
171 "%b %Y",
172 )
172 )
173
173
174 def cachefunc(func):
174 def cachefunc(func):
175 '''cache the result of function calls'''
175 '''cache the result of function calls'''
176 # XXX doesn't handle keywords args
176 # XXX doesn't handle keywords args
177 cache = {}
177 cache = {}
178 if func.func_code.co_argcount == 1:
178 if func.func_code.co_argcount == 1:
179 # we gain a small amount of time because
179 # we gain a small amount of time because
180 # we don't need to pack/unpack the list
180 # we don't need to pack/unpack the list
181 def f(arg):
181 def f(arg):
182 if arg not in cache:
182 if arg not in cache:
183 cache[arg] = func(arg)
183 cache[arg] = func(arg)
184 return cache[arg]
184 return cache[arg]
185 else:
185 else:
186 def f(*args):
186 def f(*args):
187 if args not in cache:
187 if args not in cache:
188 cache[args] = func(*args)
188 cache[args] = func(*args)
189 return cache[args]
189 return cache[args]
190
190
191 return f
191 return f
192
192
193 def lrucachefunc(func):
193 def lrucachefunc(func):
194 '''cache most recent results of function calls'''
194 '''cache most recent results of function calls'''
195 cache = {}
195 cache = {}
196 order = []
196 order = []
197 if func.func_code.co_argcount == 1:
197 if func.func_code.co_argcount == 1:
198 def f(arg):
198 def f(arg):
199 if arg not in cache:
199 if arg not in cache:
200 if len(cache) > 20:
200 if len(cache) > 20:
201 del cache[order.pop(0)]
201 del cache[order.pop(0)]
202 cache[arg] = func(arg)
202 cache[arg] = func(arg)
203 else:
203 else:
204 order.remove(arg)
204 order.remove(arg)
205 order.append(arg)
205 order.append(arg)
206 return cache[arg]
206 return cache[arg]
207 else:
207 else:
208 def f(*args):
208 def f(*args):
209 if args not in cache:
209 if args not in cache:
210 if len(cache) > 20:
210 if len(cache) > 20:
211 del cache[order.pop(0)]
211 del cache[order.pop(0)]
212 cache[args] = func(*args)
212 cache[args] = func(*args)
213 else:
213 else:
214 order.remove(args)
214 order.remove(args)
215 order.append(args)
215 order.append(args)
216 return cache[args]
216 return cache[args]
217
217
218 return f
218 return f
219
219
220 class propertycache(object):
220 class propertycache(object):
221 def __init__(self, func):
221 def __init__(self, func):
222 self.func = func
222 self.func = func
223 self.name = func.__name__
223 self.name = func.__name__
224 def __get__(self, obj, type=None):
224 def __get__(self, obj, type=None):
225 result = self.func(obj)
225 result = self.func(obj)
226 setattr(obj, self.name, result)
226 setattr(obj, self.name, result)
227 return result
227 return result
228
228
229 def pipefilter(s, cmd):
229 def pipefilter(s, cmd):
230 '''filter string S through command CMD, returning its output'''
230 '''filter string S through command CMD, returning its output'''
231 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
231 p = subprocess.Popen(cmd, shell=True, close_fds=closefds,
232 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
232 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
233 pout, perr = p.communicate(s)
233 pout, perr = p.communicate(s)
234 return pout
234 return pout
235
235
236 def tempfilter(s, cmd):
236 def tempfilter(s, cmd):
237 '''filter string S through a pair of temporary files with CMD.
237 '''filter string S through a pair of temporary files with CMD.
238 CMD is used as a template to create the real command to be run,
238 CMD is used as a template to create the real command to be run,
239 with the strings INFILE and OUTFILE replaced by the real names of
239 with the strings INFILE and OUTFILE replaced by the real names of
240 the temporary files generated.'''
240 the temporary files generated.'''
241 inname, outname = None, None
241 inname, outname = None, None
242 try:
242 try:
243 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
243 infd, inname = tempfile.mkstemp(prefix='hg-filter-in-')
244 fp = os.fdopen(infd, 'wb')
244 fp = os.fdopen(infd, 'wb')
245 fp.write(s)
245 fp.write(s)
246 fp.close()
246 fp.close()
247 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
247 outfd, outname = tempfile.mkstemp(prefix='hg-filter-out-')
248 os.close(outfd)
248 os.close(outfd)
249 cmd = cmd.replace('INFILE', inname)
249 cmd = cmd.replace('INFILE', inname)
250 cmd = cmd.replace('OUTFILE', outname)
250 cmd = cmd.replace('OUTFILE', outname)
251 code = os.system(cmd)
251 code = os.system(cmd)
252 if sys.platform == 'OpenVMS' and code & 1:
252 if sys.platform == 'OpenVMS' and code & 1:
253 code = 0
253 code = 0
254 if code:
254 if code:
255 raise Abort(_("command '%s' failed: %s") %
255 raise Abort(_("command '%s' failed: %s") %
256 (cmd, explainexit(code)))
256 (cmd, explainexit(code)))
257 fp = open(outname, 'rb')
257 fp = open(outname, 'rb')
258 r = fp.read()
258 r = fp.read()
259 fp.close()
259 fp.close()
260 return r
260 return r
261 finally:
261 finally:
262 try:
262 try:
263 if inname:
263 if inname:
264 os.unlink(inname)
264 os.unlink(inname)
265 except OSError:
265 except OSError:
266 pass
266 pass
267 try:
267 try:
268 if outname:
268 if outname:
269 os.unlink(outname)
269 os.unlink(outname)
270 except OSError:
270 except OSError:
271 pass
271 pass
272
272
273 filtertable = {
273 filtertable = {
274 'tempfile:': tempfilter,
274 'tempfile:': tempfilter,
275 'pipe:': pipefilter,
275 'pipe:': pipefilter,
276 }
276 }
277
277
278 def filter(s, cmd):
278 def filter(s, cmd):
279 "filter a string through a command that transforms its input to its output"
279 "filter a string through a command that transforms its input to its output"
280 for name, fn in filtertable.iteritems():
280 for name, fn in filtertable.iteritems():
281 if cmd.startswith(name):
281 if cmd.startswith(name):
282 return fn(s, cmd[len(name):].lstrip())
282 return fn(s, cmd[len(name):].lstrip())
283 return pipefilter(s, cmd)
283 return pipefilter(s, cmd)
284
284
285 def binary(s):
285 def binary(s):
286 """return true if a string is binary data"""
286 """return true if a string is binary data"""
287 return bool(s and '\0' in s)
287 return bool(s and '\0' in s)
288
288
289 def increasingchunks(source, min=1024, max=65536):
289 def increasingchunks(source, min=1024, max=65536):
290 '''return no less than min bytes per chunk while data remains,
290 '''return no less than min bytes per chunk while data remains,
291 doubling min after each chunk until it reaches max'''
291 doubling min after each chunk until it reaches max'''
292 def log2(x):
292 def log2(x):
293 if not x:
293 if not x:
294 return 0
294 return 0
295 i = 0
295 i = 0
296 while x:
296 while x:
297 x >>= 1
297 x >>= 1
298 i += 1
298 i += 1
299 return i - 1
299 return i - 1
300
300
301 buf = []
301 buf = []
302 blen = 0
302 blen = 0
303 for chunk in source:
303 for chunk in source:
304 buf.append(chunk)
304 buf.append(chunk)
305 blen += len(chunk)
305 blen += len(chunk)
306 if blen >= min:
306 if blen >= min:
307 if min < max:
307 if min < max:
308 min = min << 1
308 min = min << 1
309 nmin = 1 << log2(blen)
309 nmin = 1 << log2(blen)
310 if nmin > min:
310 if nmin > min:
311 min = nmin
311 min = nmin
312 if min > max:
312 if min > max:
313 min = max
313 min = max
314 yield ''.join(buf)
314 yield ''.join(buf)
315 blen = 0
315 blen = 0
316 buf = []
316 buf = []
317 if buf:
317 if buf:
318 yield ''.join(buf)
318 yield ''.join(buf)
319
319
320 Abort = error.Abort
320 Abort = error.Abort
321
321
322 def always(fn):
322 def always(fn):
323 return True
323 return True
324
324
325 def never(fn):
325 def never(fn):
326 return False
326 return False
327
327
328 def pathto(root, n1, n2):
328 def pathto(root, n1, n2):
329 '''return the relative path from one place to another.
329 '''return the relative path from one place to another.
330 root should use os.sep to separate directories
330 root should use os.sep to separate directories
331 n1 should use os.sep to separate directories
331 n1 should use os.sep to separate directories
332 n2 should use "/" to separate directories
332 n2 should use "/" to separate directories
333 returns an os.sep-separated path.
333 returns an os.sep-separated path.
334
334
335 If n1 is a relative path, it's assumed it's
335 If n1 is a relative path, it's assumed it's
336 relative to root.
336 relative to root.
337 n2 should always be relative to root.
337 n2 should always be relative to root.
338 '''
338 '''
339 if not n1:
339 if not n1:
340 return localpath(n2)
340 return localpath(n2)
341 if os.path.isabs(n1):
341 if os.path.isabs(n1):
342 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
342 if os.path.splitdrive(root)[0] != os.path.splitdrive(n1)[0]:
343 return os.path.join(root, localpath(n2))
343 return os.path.join(root, localpath(n2))
344 n2 = '/'.join((pconvert(root), n2))
344 n2 = '/'.join((pconvert(root), n2))
345 a, b = splitpath(n1), n2.split('/')
345 a, b = splitpath(n1), n2.split('/')
346 a.reverse()
346 a.reverse()
347 b.reverse()
347 b.reverse()
348 while a and b and a[-1] == b[-1]:
348 while a and b and a[-1] == b[-1]:
349 a.pop()
349 a.pop()
350 b.pop()
350 b.pop()
351 b.reverse()
351 b.reverse()
352 return os.sep.join((['..'] * len(a)) + b) or '.'
352 return os.sep.join((['..'] * len(a)) + b) or '.'
353
353
354 _hgexecutable = None
354 _hgexecutable = None
355
355
356 def mainfrozen():
356 def mainfrozen():
357 """return True if we are a frozen executable.
357 """return True if we are a frozen executable.
358
358
359 The code supports py2exe (most common, Windows only) and tools/freeze
359 The code supports py2exe (most common, Windows only) and tools/freeze
360 (portable, not much used).
360 (portable, not much used).
361 """
361 """
362 return (safehasattr(sys, "frozen") or # new py2exe
362 return (safehasattr(sys, "frozen") or # new py2exe
363 safehasattr(sys, "importers") or # old py2exe
363 safehasattr(sys, "importers") or # old py2exe
364 imp.is_frozen("__main__")) # tools/freeze
364 imp.is_frozen("__main__")) # tools/freeze
365
365
366 def hgexecutable():
366 def hgexecutable():
367 """return location of the 'hg' executable.
367 """return location of the 'hg' executable.
368
368
369 Defaults to $HG or 'hg' in the search path.
369 Defaults to $HG or 'hg' in the search path.
370 """
370 """
371 if _hgexecutable is None:
371 if _hgexecutable is None:
372 hg = os.environ.get('HG')
372 hg = os.environ.get('HG')
373 if hg:
373 if hg:
374 _sethgexecutable(hg)
374 _sethgexecutable(hg)
375 elif mainfrozen():
375 elif mainfrozen():
376 _sethgexecutable(sys.executable)
376 _sethgexecutable(sys.executable)
377 elif getattr(sys.modules['__main__'], '__file__', '').endswith('hg'):
377 elif getattr(sys.modules['__main__'], '__file__', '').endswith('hg'):
378 _sethgexecutable(sys.modules['__main__'].__file__)
378 _sethgexecutable(sys.modules['__main__'].__file__)
379 else:
379 else:
380 exe = findexe('hg') or os.path.basename(sys.argv[0])
380 exe = findexe('hg') or os.path.basename(sys.argv[0])
381 _sethgexecutable(exe)
381 _sethgexecutable(exe)
382 return _hgexecutable
382 return _hgexecutable
383
383
384 def _sethgexecutable(path):
384 def _sethgexecutable(path):
385 """set location of the 'hg' executable"""
385 """set location of the 'hg' executable"""
386 global _hgexecutable
386 global _hgexecutable
387 _hgexecutable = path
387 _hgexecutable = path
388
388
389 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
389 def system(cmd, environ={}, cwd=None, onerr=None, errprefix=None, out=None):
390 '''enhanced shell command execution.
390 '''enhanced shell command execution.
391 run with environment maybe modified, maybe in different dir.
391 run with environment maybe modified, maybe in different dir.
392
392
393 if command fails and onerr is None, return status. if ui object,
393 if command fails and onerr is None, return status. if ui object,
394 print error message and return status, else raise onerr object as
394 print error message and return status, else raise onerr object as
395 exception.
395 exception.
396
396
397 if out is specified, it is assumed to be a file-like object that has a
397 if out is specified, it is assumed to be a file-like object that has a
398 write() method. stdout and stderr will be redirected to out.'''
398 write() method. stdout and stderr will be redirected to out.'''
399 try:
399 try:
400 sys.stdout.flush()
400 sys.stdout.flush()
401 except Exception:
401 except Exception:
402 pass
402 pass
403 def py2shell(val):
403 def py2shell(val):
404 'convert python object into string that is useful to shell'
404 'convert python object into string that is useful to shell'
405 if val is None or val is False:
405 if val is None or val is False:
406 return '0'
406 return '0'
407 if val is True:
407 if val is True:
408 return '1'
408 return '1'
409 return str(val)
409 return str(val)
410 origcmd = cmd
410 origcmd = cmd
411 cmd = quotecommand(cmd)
411 cmd = quotecommand(cmd)
412 env = dict(os.environ)
412 env = dict(os.environ)
413 env.update((k, py2shell(v)) for k, v in environ.iteritems())
413 env.update((k, py2shell(v)) for k, v in environ.iteritems())
414 env['HG'] = hgexecutable()
414 env['HG'] = hgexecutable()
415 if out is None or out == sys.__stdout__:
415 if out is None or out == sys.__stdout__:
416 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
416 rc = subprocess.call(cmd, shell=True, close_fds=closefds,
417 env=env, cwd=cwd)
417 env=env, cwd=cwd)
418 else:
418 else:
419 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
419 proc = subprocess.Popen(cmd, shell=True, close_fds=closefds,
420 env=env, cwd=cwd, stdout=subprocess.PIPE,
420 env=env, cwd=cwd, stdout=subprocess.PIPE,
421 stderr=subprocess.STDOUT)
421 stderr=subprocess.STDOUT)
422 for line in proc.stdout:
422 for line in proc.stdout:
423 out.write(line)
423 out.write(line)
424 proc.wait()
424 proc.wait()
425 rc = proc.returncode
425 rc = proc.returncode
426 if sys.platform == 'OpenVMS' and rc & 1:
426 if sys.platform == 'OpenVMS' and rc & 1:
427 rc = 0
427 rc = 0
428 if rc and onerr:
428 if rc and onerr:
429 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
429 errmsg = '%s %s' % (os.path.basename(origcmd.split(None, 1)[0]),
430 explainexit(rc)[0])
430 explainexit(rc)[0])
431 if errprefix:
431 if errprefix:
432 errmsg = '%s: %s' % (errprefix, errmsg)
432 errmsg = '%s: %s' % (errprefix, errmsg)
433 try:
433 try:
434 onerr.warn(errmsg + '\n')
434 onerr.warn(errmsg + '\n')
435 except AttributeError:
435 except AttributeError:
436 raise onerr(errmsg)
436 raise onerr(errmsg)
437 return rc
437 return rc
438
438
439 def checksignature(func):
439 def checksignature(func):
440 '''wrap a function with code to check for calling errors'''
440 '''wrap a function with code to check for calling errors'''
441 def check(*args, **kwargs):
441 def check(*args, **kwargs):
442 try:
442 try:
443 return func(*args, **kwargs)
443 return func(*args, **kwargs)
444 except TypeError:
444 except TypeError:
445 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
445 if len(traceback.extract_tb(sys.exc_info()[2])) == 1:
446 raise error.SignatureError
446 raise error.SignatureError
447 raise
447 raise
448
448
449 return check
449 return check
450
450
451 def copyfile(src, dest):
451 def copyfile(src, dest):
452 "copy a file, preserving mode and atime/mtime"
452 "copy a file, preserving mode and atime/mtime"
453 if os.path.islink(src):
453 if os.path.islink(src):
454 try:
454 try:
455 os.unlink(dest)
455 os.unlink(dest)
456 except OSError:
456 except OSError:
457 pass
457 pass
458 os.symlink(os.readlink(src), dest)
458 os.symlink(os.readlink(src), dest)
459 else:
459 else:
460 try:
460 try:
461 shutil.copyfile(src, dest)
461 shutil.copyfile(src, dest)
462 shutil.copymode(src, dest)
462 shutil.copymode(src, dest)
463 except shutil.Error, inst:
463 except shutil.Error, inst:
464 raise Abort(str(inst))
464 raise Abort(str(inst))
465
465
466 def copyfiles(src, dst, hardlink=None):
466 def copyfiles(src, dst, hardlink=None):
467 """Copy a directory tree using hardlinks if possible"""
467 """Copy a directory tree using hardlinks if possible"""
468
468
469 if hardlink is None:
469 if hardlink is None:
470 hardlink = (os.stat(src).st_dev ==
470 hardlink = (os.stat(src).st_dev ==
471 os.stat(os.path.dirname(dst)).st_dev)
471 os.stat(os.path.dirname(dst)).st_dev)
472
472
473 num = 0
473 num = 0
474 if os.path.isdir(src):
474 if os.path.isdir(src):
475 os.mkdir(dst)
475 os.mkdir(dst)
476 for name, kind in osutil.listdir(src):
476 for name, kind in osutil.listdir(src):
477 srcname = os.path.join(src, name)
477 srcname = os.path.join(src, name)
478 dstname = os.path.join(dst, name)
478 dstname = os.path.join(dst, name)
479 hardlink, n = copyfiles(srcname, dstname, hardlink)
479 hardlink, n = copyfiles(srcname, dstname, hardlink)
480 num += n
480 num += n
481 else:
481 else:
482 if hardlink:
482 if hardlink:
483 try:
483 try:
484 oslink(src, dst)
484 oslink(src, dst)
485 except (IOError, OSError):
485 except (IOError, OSError):
486 hardlink = False
486 hardlink = False
487 shutil.copy(src, dst)
487 shutil.copy(src, dst)
488 else:
488 else:
489 shutil.copy(src, dst)
489 shutil.copy(src, dst)
490 num += 1
490 num += 1
491
491
492 return hardlink, num
492 return hardlink, num
493
493
494 _winreservednames = '''con prn aux nul
494 _winreservednames = '''con prn aux nul
495 com1 com2 com3 com4 com5 com6 com7 com8 com9
495 com1 com2 com3 com4 com5 com6 com7 com8 com9
496 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
496 lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9'''.split()
497 _winreservedchars = ':*?"<>|'
497 _winreservedchars = ':*?"<>|'
498 def checkwinfilename(path):
498 def checkwinfilename(path):
499 '''Check that the base-relative path is a valid filename on Windows.
499 '''Check that the base-relative path is a valid filename on Windows.
500 Returns None if the path is ok, or a UI string describing the problem.
500 Returns None if the path is ok, or a UI string describing the problem.
501
501
502 >>> checkwinfilename("just/a/normal/path")
502 >>> checkwinfilename("just/a/normal/path")
503 >>> checkwinfilename("foo/bar/con.xml")
503 >>> checkwinfilename("foo/bar/con.xml")
504 "filename contains 'con', which is reserved on Windows"
504 "filename contains 'con', which is reserved on Windows"
505 >>> checkwinfilename("foo/con.xml/bar")
505 >>> checkwinfilename("foo/con.xml/bar")
506 "filename contains 'con', which is reserved on Windows"
506 "filename contains 'con', which is reserved on Windows"
507 >>> checkwinfilename("foo/bar/xml.con")
507 >>> checkwinfilename("foo/bar/xml.con")
508 >>> checkwinfilename("foo/bar/AUX/bla.txt")
508 >>> checkwinfilename("foo/bar/AUX/bla.txt")
509 "filename contains 'AUX', which is reserved on Windows"
509 "filename contains 'AUX', which is reserved on Windows"
510 >>> checkwinfilename("foo/bar/bla:.txt")
510 >>> checkwinfilename("foo/bar/bla:.txt")
511 "filename contains ':', which is reserved on Windows"
511 "filename contains ':', which is reserved on Windows"
512 >>> checkwinfilename("foo/bar/b\07la.txt")
512 >>> checkwinfilename("foo/bar/b\07la.txt")
513 "filename contains '\\\\x07', which is invalid on Windows"
513 "filename contains '\\\\x07', which is invalid on Windows"
514 >>> checkwinfilename("foo/bar/bla ")
514 >>> checkwinfilename("foo/bar/bla ")
515 "filename ends with ' ', which is not allowed on Windows"
515 "filename ends with ' ', which is not allowed on Windows"
516 '''
516 '''
517 for n in path.replace('\\', '/').split('/'):
517 for n in path.replace('\\', '/').split('/'):
518 if not n:
518 if not n:
519 continue
519 continue
520 for c in n:
520 for c in n:
521 if c in _winreservedchars:
521 if c in _winreservedchars:
522 return _("filename contains '%s', which is reserved "
522 return _("filename contains '%s', which is reserved "
523 "on Windows") % c
523 "on Windows") % c
524 if ord(c) <= 31:
524 if ord(c) <= 31:
525 return _("filename contains %r, which is invalid "
525 return _("filename contains %r, which is invalid "
526 "on Windows") % c
526 "on Windows") % c
527 base = n.split('.')[0]
527 base = n.split('.')[0]
528 if base and base.lower() in _winreservednames:
528 if base and base.lower() in _winreservednames:
529 return _("filename contains '%s', which is reserved "
529 return _("filename contains '%s', which is reserved "
530 "on Windows") % base
530 "on Windows") % base
531 t = n[-1]
531 t = n[-1]
532 if t in '. ':
532 if t in '. ':
533 return _("filename ends with '%s', which is not allowed "
533 return _("filename ends with '%s', which is not allowed "
534 "on Windows") % t
534 "on Windows") % t
535
535
536 if os.name == 'nt':
536 if os.name == 'nt':
537 checkosfilename = checkwinfilename
537 checkosfilename = checkwinfilename
538 else:
538 else:
539 checkosfilename = platform.checkosfilename
539 checkosfilename = platform.checkosfilename
540
540
541 def makelock(info, pathname):
541 def makelock(info, pathname):
542 try:
542 try:
543 return os.symlink(info, pathname)
543 return os.symlink(info, pathname)
544 except OSError, why:
544 except OSError, why:
545 if why.errno == errno.EEXIST:
545 if why.errno == errno.EEXIST:
546 raise
546 raise
547 except AttributeError: # no symlink in os
547 except AttributeError: # no symlink in os
548 pass
548 pass
549
549
550 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
550 ld = os.open(pathname, os.O_CREAT | os.O_WRONLY | os.O_EXCL)
551 os.write(ld, info)
551 os.write(ld, info)
552 os.close(ld)
552 os.close(ld)
553
553
554 def readlock(pathname):
554 def readlock(pathname):
555 try:
555 try:
556 return os.readlink(pathname)
556 return os.readlink(pathname)
557 except OSError, why:
557 except OSError, why:
558 if why.errno not in (errno.EINVAL, errno.ENOSYS):
558 if why.errno not in (errno.EINVAL, errno.ENOSYS):
559 raise
559 raise
560 except AttributeError: # no symlink in os
560 except AttributeError: # no symlink in os
561 pass
561 pass
562 fp = posixfile(pathname)
562 fp = posixfile(pathname)
563 r = fp.read()
563 r = fp.read()
564 fp.close()
564 fp.close()
565 return r
565 return r
566
566
567 def fstat(fp):
567 def fstat(fp):
568 '''stat file object that may not have fileno method.'''
568 '''stat file object that may not have fileno method.'''
569 try:
569 try:
570 return os.fstat(fp.fileno())
570 return os.fstat(fp.fileno())
571 except AttributeError:
571 except AttributeError:
572 return os.stat(fp.name)
572 return os.stat(fp.name)
573
573
574 # File system features
574 # File system features
575
575
576 def checkcase(path):
576 def checkcase(path):
577 """
577 """
578 Check whether the given path is on a case-sensitive filesystem
578 Check whether the given path is on a case-sensitive filesystem
579
579
580 Requires a path (like /foo/.hg) ending with a foldable final
580 Requires a path (like /foo/.hg) ending with a foldable final
581 directory component.
581 directory component.
582 """
582 """
583 s1 = os.stat(path)
583 s1 = os.stat(path)
584 d, b = os.path.split(path)
584 d, b = os.path.split(path)
585 p2 = os.path.join(d, b.upper())
585 p2 = os.path.join(d, b.upper())
586 if path == p2:
586 if path == p2:
587 p2 = os.path.join(d, b.lower())
587 p2 = os.path.join(d, b.lower())
588 try:
588 try:
589 s2 = os.stat(p2)
589 s2 = os.stat(p2)
590 if s2 == s1:
590 if s2 == s1:
591 return False
591 return False
592 return True
592 return True
593 except OSError:
593 except OSError:
594 return True
594 return True
595
595
596 _fspathcache = {}
596 _fspathcache = {}
597 def fspath(name, root):
597 def fspath(name, root):
598 '''Get name in the case stored in the filesystem
598 '''Get name in the case stored in the filesystem
599
599
600 The name is either relative to root, or it is an absolute path starting
600 The name is either relative to root, or it is an absolute path starting
601 with root. Note that this function is unnecessary, and should not be
601 with root. Note that this function is unnecessary, and should not be
602 called, for case-sensitive filesystems (simply because it's expensive).
602 called, for case-sensitive filesystems (simply because it's expensive).
603 '''
603 '''
604 # If name is absolute, make it relative
604 # If name is absolute, make it relative
605 if name.lower().startswith(root.lower()):
605 if name.lower().startswith(root.lower()):
606 l = len(root)
606 l = len(root)
607 if name[l] == os.sep or name[l] == os.altsep:
607 if name[l] == os.sep or name[l] == os.altsep:
608 l = l + 1
608 l = l + 1
609 name = name[l:]
609 name = name[l:]
610
610
611 if not os.path.lexists(os.path.join(root, name)):
611 if not os.path.lexists(os.path.join(root, name)):
612 return None
612 return None
613
613
614 seps = os.sep
614 seps = os.sep
615 if os.altsep:
615 if os.altsep:
616 seps = seps + os.altsep
616 seps = seps + os.altsep
617 # Protect backslashes. This gets silly very quickly.
617 # Protect backslashes. This gets silly very quickly.
618 seps.replace('\\','\\\\')
618 seps.replace('\\','\\\\')
619 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
619 pattern = re.compile(r'([^%s]+)|([%s]+)' % (seps, seps))
620 dir = os.path.normcase(os.path.normpath(root))
620 dir = os.path.normcase(os.path.normpath(root))
621 result = []
621 result = []
622 for part, sep in pattern.findall(name):
622 for part, sep in pattern.findall(name):
623 if sep:
623 if sep:
624 result.append(sep)
624 result.append(sep)
625 continue
625 continue
626
626
627 if dir not in _fspathcache:
627 if dir not in _fspathcache:
628 _fspathcache[dir] = os.listdir(dir)
628 _fspathcache[dir] = os.listdir(dir)
629 contents = _fspathcache[dir]
629 contents = _fspathcache[dir]
630
630
631 lpart = part.lower()
631 lpart = part.lower()
632 lenp = len(part)
632 lenp = len(part)
633 for n in contents:
633 for n in contents:
634 if lenp == len(n) and n.lower() == lpart:
634 if lenp == len(n) and n.lower() == lpart:
635 result.append(n)
635 result.append(n)
636 break
636 break
637 else:
637 else:
638 # Cannot happen, as the file exists!
638 # Cannot happen, as the file exists!
639 result.append(part)
639 result.append(part)
640 dir = os.path.join(dir, lpart)
640 dir = os.path.join(dir, lpart)
641
641
642 return ''.join(result)
642 return ''.join(result)
643
643
644 def checknlink(testfile):
644 def checknlink(testfile):
645 '''check whether hardlink count reporting works properly'''
645 '''check whether hardlink count reporting works properly'''
646
646
647 # testfile may be open, so we need a separate file for checking to
647 # testfile may be open, so we need a separate file for checking to
648 # work around issue2543 (or testfile may get lost on Samba shares)
648 # work around issue2543 (or testfile may get lost on Samba shares)
649 f1 = testfile + ".hgtmp1"
649 f1 = testfile + ".hgtmp1"
650 if os.path.lexists(f1):
650 if os.path.lexists(f1):
651 return False
651 return False
652 try:
652 try:
653 posixfile(f1, 'w').close()
653 posixfile(f1, 'w').close()
654 except IOError:
654 except IOError:
655 return False
655 return False
656
656
657 f2 = testfile + ".hgtmp2"
657 f2 = testfile + ".hgtmp2"
658 fd = None
658 fd = None
659 try:
659 try:
660 try:
660 try:
661 oslink(f1, f2)
661 oslink(f1, f2)
662 except OSError:
662 except OSError:
663 return False
663 return False
664
664
665 # nlinks() may behave differently for files on Windows shares if
665 # nlinks() may behave differently for files on Windows shares if
666 # the file is open.
666 # the file is open.
667 fd = posixfile(f2)
667 fd = posixfile(f2)
668 return nlinks(f2) > 1
668 return nlinks(f2) > 1
669 finally:
669 finally:
670 if fd is not None:
670 if fd is not None:
671 fd.close()
671 fd.close()
672 for f in (f1, f2):
672 for f in (f1, f2):
673 try:
673 try:
674 os.unlink(f)
674 os.unlink(f)
675 except OSError:
675 except OSError:
676 pass
676 pass
677
677
678 return False
678 return False
679
679
680 def endswithsep(path):
680 def endswithsep(path):
681 '''Check path ends with os.sep or os.altsep.'''
681 '''Check path ends with os.sep or os.altsep.'''
682 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
682 return path.endswith(os.sep) or os.altsep and path.endswith(os.altsep)
683
683
684 def splitpath(path):
684 def splitpath(path):
685 '''Split path by os.sep.
685 '''Split path by os.sep.
686 Note that this function does not use os.altsep because this is
686 Note that this function does not use os.altsep because this is
687 an alternative of simple "xxx.split(os.sep)".
687 an alternative of simple "xxx.split(os.sep)".
688 It is recommended to use os.path.normpath() before using this
688 It is recommended to use os.path.normpath() before using this
689 function if need.'''
689 function if need.'''
690 return path.split(os.sep)
690 return path.split(os.sep)
691
691
692 def gui():
692 def gui():
693 '''Are we running in a GUI?'''
693 '''Are we running in a GUI?'''
694 if sys.platform == 'darwin':
694 if sys.platform == 'darwin':
695 if 'SSH_CONNECTION' in os.environ:
695 if 'SSH_CONNECTION' in os.environ:
696 # handle SSH access to a box where the user is logged in
696 # handle SSH access to a box where the user is logged in
697 return False
697 return False
698 elif getattr(osutil, 'isgui', None):
698 elif getattr(osutil, 'isgui', None):
699 # check if a CoreGraphics session is available
699 # check if a CoreGraphics session is available
700 return osutil.isgui()
700 return osutil.isgui()
701 else:
701 else:
702 # pure build; use a safe default
702 # pure build; use a safe default
703 return True
703 return True
704 else:
704 else:
705 return os.name == "nt" or os.environ.get("DISPLAY")
705 return os.name == "nt" or os.environ.get("DISPLAY")
706
706
707 def mktempcopy(name, emptyok=False, createmode=None):
707 def mktempcopy(name, emptyok=False, createmode=None):
708 """Create a temporary file with the same contents from name
708 """Create a temporary file with the same contents from name
709
709
710 The permission bits are copied from the original file.
710 The permission bits are copied from the original file.
711
711
712 If the temporary file is going to be truncated immediately, you
712 If the temporary file is going to be truncated immediately, you
713 can use emptyok=True as an optimization.
713 can use emptyok=True as an optimization.
714
714
715 Returns the name of the temporary file.
715 Returns the name of the temporary file.
716 """
716 """
717 d, fn = os.path.split(name)
717 d, fn = os.path.split(name)
718 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
718 fd, temp = tempfile.mkstemp(prefix='.%s-' % fn, dir=d)
719 os.close(fd)
719 os.close(fd)
720 # Temporary files are created with mode 0600, which is usually not
720 # Temporary files are created with mode 0600, which is usually not
721 # what we want. If the original file already exists, just copy
721 # what we want. If the original file already exists, just copy
722 # its mode. Otherwise, manually obey umask.
722 # its mode. Otherwise, manually obey umask.
723 copymode(name, temp, createmode)
723 copymode(name, temp, createmode)
724 if emptyok:
724 if emptyok:
725 return temp
725 return temp
726 try:
726 try:
727 try:
727 try:
728 ifp = posixfile(name, "rb")
728 ifp = posixfile(name, "rb")
729 except IOError, inst:
729 except IOError, inst:
730 if inst.errno == errno.ENOENT:
730 if inst.errno == errno.ENOENT:
731 return temp
731 return temp
732 if not getattr(inst, 'filename', None):
732 if not getattr(inst, 'filename', None):
733 inst.filename = name
733 inst.filename = name
734 raise
734 raise
735 ofp = posixfile(temp, "wb")
735 ofp = posixfile(temp, "wb")
736 for chunk in filechunkiter(ifp):
736 for chunk in filechunkiter(ifp):
737 ofp.write(chunk)
737 ofp.write(chunk)
738 ifp.close()
738 ifp.close()
739 ofp.close()
739 ofp.close()
740 except:
740 except:
741 try: os.unlink(temp)
741 try: os.unlink(temp)
742 except: pass
742 except: pass
743 raise
743 raise
744 return temp
744 return temp
745
745
746 class atomictempfile(object):
746 class atomictempfile(object):
747 '''writeable file object that atomically updates a file
747 '''writeable file object that atomically updates a file
748
748
749 All writes will go to a temporary copy of the original file. Call
749 All writes will go to a temporary copy of the original file. Call
750 close() when you are done writing, and atomictempfile will rename
750 close() when you are done writing, and atomictempfile will rename
751 the temporary copy to the original name, making the changes
751 the temporary copy to the original name, making the changes
752 visible. If the object is destroyed without being closed, all your
752 visible. If the object is destroyed without being closed, all your
753 writes are discarded.
753 writes are discarded.
754 '''
754 '''
755 def __init__(self, name, mode='w+b', createmode=None):
755 def __init__(self, name, mode='w+b', createmode=None):
756 self.__name = name # permanent name
756 self.__name = name # permanent name
757 self._tempname = mktempcopy(name, emptyok=('w' in mode),
757 self._tempname = mktempcopy(name, emptyok=('w' in mode),
758 createmode=createmode)
758 createmode=createmode)
759 self._fp = posixfile(self._tempname, mode)
759 self._fp = posixfile(self._tempname, mode)
760
760
761 # delegated methods
761 # delegated methods
762 self.write = self._fp.write
762 self.write = self._fp.write
763 self.fileno = self._fp.fileno
763 self.fileno = self._fp.fileno
764
764
765 def close(self):
765 def close(self):
766 if not self._fp.closed:
766 if not self._fp.closed:
767 self._fp.close()
767 self._fp.close()
768 rename(self._tempname, localpath(self.__name))
768 rename(self._tempname, localpath(self.__name))
769
769
770 def discard(self):
770 def discard(self):
771 if not self._fp.closed:
771 if not self._fp.closed:
772 try:
772 try:
773 os.unlink(self._tempname)
773 os.unlink(self._tempname)
774 except OSError:
774 except OSError:
775 pass
775 pass
776 self._fp.close()
776 self._fp.close()
777
777
778 def __del__(self):
778 def __del__(self):
779 if safehasattr(self, '_fp'): # constructor actually did something
779 if safehasattr(self, '_fp'): # constructor actually did something
780 self.discard()
780 self.discard()
781
781
782 def makedirs(name, mode=None):
782 def makedirs(name, mode=None):
783 """recursive directory creation with parent mode inheritance"""
783 """recursive directory creation with parent mode inheritance"""
784 try:
784 try:
785 os.mkdir(name)
785 os.mkdir(name)
786 except OSError, err:
786 except OSError, err:
787 if err.errno == errno.EEXIST:
787 if err.errno == errno.EEXIST:
788 return
788 return
789 if err.errno != errno.ENOENT or not name:
789 if err.errno != errno.ENOENT or not name:
790 raise
790 raise
791 parent = os.path.dirname(os.path.abspath(name))
791 parent = os.path.dirname(os.path.abspath(name))
792 if parent == name:
792 if parent == name:
793 raise
793 raise
794 makedirs(parent, mode)
794 makedirs(parent, mode)
795 os.mkdir(name)
795 os.mkdir(name)
796 if mode is not None:
796 if mode is not None:
797 os.chmod(name, mode)
797 os.chmod(name, mode)
798
798
799 def readfile(path):
799 def readfile(path):
800 fp = open(path, 'rb')
800 fp = open(path, 'rb')
801 try:
801 try:
802 return fp.read()
802 return fp.read()
803 finally:
803 finally:
804 fp.close()
804 fp.close()
805
805
806 def writefile(path, text):
806 def writefile(path, text):
807 fp = open(path, 'wb')
807 fp = open(path, 'wb')
808 try:
808 try:
809 fp.write(text)
809 fp.write(text)
810 finally:
810 finally:
811 fp.close()
811 fp.close()
812
812
813 def appendfile(path, text):
813 def appendfile(path, text):
814 fp = open(path, 'ab')
814 fp = open(path, 'ab')
815 try:
815 try:
816 fp.write(text)
816 fp.write(text)
817 finally:
817 finally:
818 fp.close()
818 fp.close()
819
819
820 class chunkbuffer(object):
820 class chunkbuffer(object):
821 """Allow arbitrary sized chunks of data to be efficiently read from an
821 """Allow arbitrary sized chunks of data to be efficiently read from an
822 iterator over chunks of arbitrary size."""
822 iterator over chunks of arbitrary size."""
823
823
824 def __init__(self, in_iter):
824 def __init__(self, in_iter):
825 """in_iter is the iterator that's iterating over the input chunks.
825 """in_iter is the iterator that's iterating over the input chunks.
826 targetsize is how big a buffer to try to maintain."""
826 targetsize is how big a buffer to try to maintain."""
827 def splitbig(chunks):
827 def splitbig(chunks):
828 for chunk in chunks:
828 for chunk in chunks:
829 if len(chunk) > 2**20:
829 if len(chunk) > 2**20:
830 pos = 0
830 pos = 0
831 while pos < len(chunk):
831 while pos < len(chunk):
832 end = pos + 2 ** 18
832 end = pos + 2 ** 18
833 yield chunk[pos:end]
833 yield chunk[pos:end]
834 pos = end
834 pos = end
835 else:
835 else:
836 yield chunk
836 yield chunk
837 self.iter = splitbig(in_iter)
837 self.iter = splitbig(in_iter)
838 self._queue = []
838 self._queue = []
839
839
840 def read(self, l):
840 def read(self, l):
841 """Read L bytes of data from the iterator of chunks of data.
841 """Read L bytes of data from the iterator of chunks of data.
842 Returns less than L bytes if the iterator runs dry."""
842 Returns less than L bytes if the iterator runs dry."""
843 left = l
843 left = l
844 buf = ''
844 buf = ''
845 queue = self._queue
845 queue = self._queue
846 while left > 0:
846 while left > 0:
847 # refill the queue
847 # refill the queue
848 if not queue:
848 if not queue:
849 target = 2**18
849 target = 2**18
850 for chunk in self.iter:
850 for chunk in self.iter:
851 queue.append(chunk)
851 queue.append(chunk)
852 target -= len(chunk)
852 target -= len(chunk)
853 if target <= 0:
853 if target <= 0:
854 break
854 break
855 if not queue:
855 if not queue:
856 break
856 break
857
857
858 chunk = queue.pop(0)
858 chunk = queue.pop(0)
859 left -= len(chunk)
859 left -= len(chunk)
860 if left < 0:
860 if left < 0:
861 queue.insert(0, chunk[left:])
861 queue.insert(0, chunk[left:])
862 buf += chunk[:left]
862 buf += chunk[:left]
863 else:
863 else:
864 buf += chunk
864 buf += chunk
865
865
866 return buf
866 return buf
867
867
868 def filechunkiter(f, size=65536, limit=None):
868 def filechunkiter(f, size=65536, limit=None):
869 """Create a generator that produces the data in the file size
869 """Create a generator that produces the data in the file size
870 (default 65536) bytes at a time, up to optional limit (default is
870 (default 65536) bytes at a time, up to optional limit (default is
871 to read all data). Chunks may be less than size bytes if the
871 to read all data). Chunks may be less than size bytes if the
872 chunk is the last chunk in the file, or the file is a socket or
872 chunk is the last chunk in the file, or the file is a socket or
873 some other type of file that sometimes reads less data than is
873 some other type of file that sometimes reads less data than is
874 requested."""
874 requested."""
875 assert size >= 0
875 assert size >= 0
876 assert limit is None or limit >= 0
876 assert limit is None or limit >= 0
877 while True:
877 while True:
878 if limit is None:
878 if limit is None:
879 nbytes = size
879 nbytes = size
880 else:
880 else:
881 nbytes = min(limit, size)
881 nbytes = min(limit, size)
882 s = nbytes and f.read(nbytes)
882 s = nbytes and f.read(nbytes)
883 if not s:
883 if not s:
884 break
884 break
885 if limit:
885 if limit:
886 limit -= len(s)
886 limit -= len(s)
887 yield s
887 yield s
888
888
889 def makedate():
889 def makedate():
890 lt = time.localtime()
890 lt = time.localtime()
891 if lt[8] == 1 and time.daylight:
891 if lt[8] == 1 and time.daylight:
892 tz = time.altzone
892 tz = time.altzone
893 else:
893 else:
894 tz = time.timezone
894 tz = time.timezone
895 t = time.mktime(lt)
895 t = time.mktime(lt)
896 if t < 0:
896 if t < 0:
897 hint = _("check your clock")
897 hint = _("check your clock")
898 raise Abort(_("negative timestamp: %d") % t, hint=hint)
898 raise Abort(_("negative timestamp: %d") % t, hint=hint)
899 return t, tz
899 return t, tz
900
900
901 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
901 def datestr(date=None, format='%a %b %d %H:%M:%S %Y %1%2'):
902 """represent a (unixtime, offset) tuple as a localized time.
902 """represent a (unixtime, offset) tuple as a localized time.
903 unixtime is seconds since the epoch, and offset is the time zone's
903 unixtime is seconds since the epoch, and offset is the time zone's
904 number of seconds away from UTC. if timezone is false, do not
904 number of seconds away from UTC. if timezone is false, do not
905 append time zone to string."""
905 append time zone to string."""
906 t, tz = date or makedate()
906 t, tz = date or makedate()
907 if t < 0:
907 if t < 0:
908 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
908 t = 0 # time.gmtime(lt) fails on Windows for lt < -43200
909 tz = 0
909 tz = 0
910 if "%1" in format or "%2" in format:
910 if "%1" in format or "%2" in format:
911 sign = (tz > 0) and "-" or "+"
911 sign = (tz > 0) and "-" or "+"
912 minutes = abs(tz) // 60
912 minutes = abs(tz) // 60
913 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
913 format = format.replace("%1", "%c%02d" % (sign, minutes // 60))
914 format = format.replace("%2", "%02d" % (minutes % 60))
914 format = format.replace("%2", "%02d" % (minutes % 60))
915 s = time.strftime(format, time.gmtime(float(t) - tz))
915 s = time.strftime(format, time.gmtime(float(t) - tz))
916 return s
916 return s
917
917
918 def shortdate(date=None):
918 def shortdate(date=None):
919 """turn (timestamp, tzoff) tuple into iso 8631 date."""
919 """turn (timestamp, tzoff) tuple into iso 8631 date."""
920 return datestr(date, format='%Y-%m-%d')
920 return datestr(date, format='%Y-%m-%d')
921
921
922 def strdate(string, format, defaults=[]):
922 def strdate(string, format, defaults=[]):
923 """parse a localized time string and return a (unixtime, offset) tuple.
923 """parse a localized time string and return a (unixtime, offset) tuple.
924 if the string cannot be parsed, ValueError is raised."""
924 if the string cannot be parsed, ValueError is raised."""
925 def timezone(string):
925 def timezone(string):
926 tz = string.split()[-1]
926 tz = string.split()[-1]
927 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
927 if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit():
928 sign = (tz[0] == "+") and 1 or -1
928 sign = (tz[0] == "+") and 1 or -1
929 hours = int(tz[1:3])
929 hours = int(tz[1:3])
930 minutes = int(tz[3:5])
930 minutes = int(tz[3:5])
931 return -sign * (hours * 60 + minutes) * 60
931 return -sign * (hours * 60 + minutes) * 60
932 if tz == "GMT" or tz == "UTC":
932 if tz == "GMT" or tz == "UTC":
933 return 0
933 return 0
934 return None
934 return None
935
935
936 # NOTE: unixtime = localunixtime + offset
936 # NOTE: unixtime = localunixtime + offset
937 offset, date = timezone(string), string
937 offset, date = timezone(string), string
938 if offset is not None:
938 if offset is not None:
939 date = " ".join(string.split()[:-1])
939 date = " ".join(string.split()[:-1])
940
940
941 # add missing elements from defaults
941 # add missing elements from defaults
942 usenow = False # default to using biased defaults
942 usenow = False # default to using biased defaults
943 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
943 for part in ("S", "M", "HI", "d", "mb", "yY"): # decreasing specificity
944 found = [True for p in part if ("%"+p) in format]
944 found = [True for p in part if ("%"+p) in format]
945 if not found:
945 if not found:
946 date += "@" + defaults[part][usenow]
946 date += "@" + defaults[part][usenow]
947 format += "@%" + part[0]
947 format += "@%" + part[0]
948 else:
948 else:
949 # We've found a specific time element, less specific time
949 # We've found a specific time element, less specific time
950 # elements are relative to today
950 # elements are relative to today
951 usenow = True
951 usenow = True
952
952
953 timetuple = time.strptime(date, format)
953 timetuple = time.strptime(date, format)
954 localunixtime = int(calendar.timegm(timetuple))
954 localunixtime = int(calendar.timegm(timetuple))
955 if offset is None:
955 if offset is None:
956 # local timezone
956 # local timezone
957 unixtime = int(time.mktime(timetuple))
957 unixtime = int(time.mktime(timetuple))
958 offset = unixtime - localunixtime
958 offset = unixtime - localunixtime
959 else:
959 else:
960 unixtime = localunixtime + offset
960 unixtime = localunixtime + offset
961 return unixtime, offset
961 return unixtime, offset
962
962
963 def parsedate(date, formats=None, bias={}):
963 def parsedate(date, formats=None, bias={}):
964 """parse a localized date/time and return a (unixtime, offset) tuple.
964 """parse a localized date/time and return a (unixtime, offset) tuple.
965
965
966 The date may be a "unixtime offset" string or in one of the specified
966 The date may be a "unixtime offset" string or in one of the specified
967 formats. If the date already is a (unixtime, offset) tuple, it is returned.
967 formats. If the date already is a (unixtime, offset) tuple, it is returned.
968 """
968 """
969 if not date:
969 if not date:
970 return 0, 0
970 return 0, 0
971 if isinstance(date, tuple) and len(date) == 2:
971 if isinstance(date, tuple) and len(date) == 2:
972 return date
972 return date
973 if not formats:
973 if not formats:
974 formats = defaultdateformats
974 formats = defaultdateformats
975 date = date.strip()
975 date = date.strip()
976 try:
976 try:
977 when, offset = map(int, date.split(' '))
977 when, offset = map(int, date.split(' '))
978 except ValueError:
978 except ValueError:
979 # fill out defaults
979 # fill out defaults
980 now = makedate()
980 now = makedate()
981 defaults = {}
981 defaults = {}
982 for part in ("d", "mb", "yY", "HI", "M", "S"):
982 for part in ("d", "mb", "yY", "HI", "M", "S"):
983 # this piece is for rounding the specific end of unknowns
983 # this piece is for rounding the specific end of unknowns
984 b = bias.get(part)
984 b = bias.get(part)
985 if b is None:
985 if b is None:
986 if part[0] in "HMS":
986 if part[0] in "HMS":
987 b = "00"
987 b = "00"
988 else:
988 else:
989 b = "0"
989 b = "0"
990
990
991 # this piece is for matching the generic end to today's date
991 # this piece is for matching the generic end to today's date
992 n = datestr(now, "%" + part[0])
992 n = datestr(now, "%" + part[0])
993
993
994 defaults[part] = (b, n)
994 defaults[part] = (b, n)
995
995
996 for format in formats:
996 for format in formats:
997 try:
997 try:
998 when, offset = strdate(date, format, defaults)
998 when, offset = strdate(date, format, defaults)
999 except (ValueError, OverflowError):
999 except (ValueError, OverflowError):
1000 pass
1000 pass
1001 else:
1001 else:
1002 break
1002 break
1003 else:
1003 else:
1004 raise Abort(_('invalid date: %r') % date)
1004 raise Abort(_('invalid date: %r') % date)
1005 # validate explicit (probably user-specified) date and
1005 # validate explicit (probably user-specified) date and
1006 # time zone offset. values must fit in signed 32 bits for
1006 # time zone offset. values must fit in signed 32 bits for
1007 # current 32-bit linux runtimes. timezones go from UTC-12
1007 # current 32-bit linux runtimes. timezones go from UTC-12
1008 # to UTC+14
1008 # to UTC+14
1009 if abs(when) > 0x7fffffff:
1009 if abs(when) > 0x7fffffff:
1010 raise Abort(_('date exceeds 32 bits: %d') % when)
1010 raise Abort(_('date exceeds 32 bits: %d') % when)
1011 if when < 0:
1011 if when < 0:
1012 raise Abort(_('negative date value: %d') % when)
1012 raise Abort(_('negative date value: %d') % when)
1013 if offset < -50400 or offset > 43200:
1013 if offset < -50400 or offset > 43200:
1014 raise Abort(_('impossible time zone offset: %d') % offset)
1014 raise Abort(_('impossible time zone offset: %d') % offset)
1015 return when, offset
1015 return when, offset
1016
1016
1017 def matchdate(date):
1017 def matchdate(date):
1018 """Return a function that matches a given date match specifier
1018 """Return a function that matches a given date match specifier
1019
1019
1020 Formats include:
1020 Formats include:
1021
1021
1022 '{date}' match a given date to the accuracy provided
1022 '{date}' match a given date to the accuracy provided
1023
1023
1024 '<{date}' on or before a given date
1024 '<{date}' on or before a given date
1025
1025
1026 '>{date}' on or after a given date
1026 '>{date}' on or after a given date
1027
1027
1028 >>> p1 = parsedate("10:29:59")
1028 >>> p1 = parsedate("10:29:59")
1029 >>> p2 = parsedate("10:30:00")
1029 >>> p2 = parsedate("10:30:00")
1030 >>> p3 = parsedate("10:30:59")
1030 >>> p3 = parsedate("10:30:59")
1031 >>> p4 = parsedate("10:31:00")
1031 >>> p4 = parsedate("10:31:00")
1032 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1032 >>> p5 = parsedate("Sep 15 10:30:00 1999")
1033 >>> f = matchdate("10:30")
1033 >>> f = matchdate("10:30")
1034 >>> f(p1[0])
1034 >>> f(p1[0])
1035 False
1035 False
1036 >>> f(p2[0])
1036 >>> f(p2[0])
1037 True
1037 True
1038 >>> f(p3[0])
1038 >>> f(p3[0])
1039 True
1039 True
1040 >>> f(p4[0])
1040 >>> f(p4[0])
1041 False
1041 False
1042 >>> f(p5[0])
1042 >>> f(p5[0])
1043 False
1043 False
1044 """
1044 """
1045
1045
1046 def lower(date):
1046 def lower(date):
1047 d = dict(mb="1", d="1")
1047 d = dict(mb="1", d="1")
1048 return parsedate(date, extendeddateformats, d)[0]
1048 return parsedate(date, extendeddateformats, d)[0]
1049
1049
1050 def upper(date):
1050 def upper(date):
1051 d = dict(mb="12", HI="23", M="59", S="59")
1051 d = dict(mb="12", HI="23", M="59", S="59")
1052 for days in ("31", "30", "29"):
1052 for days in ("31", "30", "29"):
1053 try:
1053 try:
1054 d["d"] = days
1054 d["d"] = days
1055 return parsedate(date, extendeddateformats, d)[0]
1055 return parsedate(date, extendeddateformats, d)[0]
1056 except:
1056 except:
1057 pass
1057 pass
1058 d["d"] = "28"
1058 d["d"] = "28"
1059 return parsedate(date, extendeddateformats, d)[0]
1059 return parsedate(date, extendeddateformats, d)[0]
1060
1060
1061 date = date.strip()
1061 date = date.strip()
1062
1062
1063 if not date:
1063 if not date:
1064 raise Abort(_("dates cannot consist entirely of whitespace"))
1064 raise Abort(_("dates cannot consist entirely of whitespace"))
1065 elif date[0] == "<":
1065 elif date[0] == "<":
1066 if not date[1:]:
1066 if not date[1:]:
1067 raise Abort(_("invalid day spec, use '<DATE'"))
1067 raise Abort(_("invalid day spec, use '<DATE'"))
1068 when = upper(date[1:])
1068 when = upper(date[1:])
1069 return lambda x: x <= when
1069 return lambda x: x <= when
1070 elif date[0] == ">":
1070 elif date[0] == ">":
1071 if not date[1:]:
1071 if not date[1:]:
1072 raise Abort(_("invalid day spec, use '>DATE'"))
1072 raise Abort(_("invalid day spec, use '>DATE'"))
1073 when = lower(date[1:])
1073 when = lower(date[1:])
1074 return lambda x: x >= when
1074 return lambda x: x >= when
1075 elif date[0] == "-":
1075 elif date[0] == "-":
1076 try:
1076 try:
1077 days = int(date[1:])
1077 days = int(date[1:])
1078 except ValueError:
1078 except ValueError:
1079 raise Abort(_("invalid day spec: %s") % date[1:])
1079 raise Abort(_("invalid day spec: %s") % date[1:])
1080 if days < 0:
1080 if days < 0:
1081 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
1081 raise Abort(_("%s must be nonnegative (see 'hg help dates')")
1082 % date[1:])
1082 % date[1:])
1083 when = makedate()[0] - days * 3600 * 24
1083 when = makedate()[0] - days * 3600 * 24
1084 return lambda x: x >= when
1084 return lambda x: x >= when
1085 elif " to " in date:
1085 elif " to " in date:
1086 a, b = date.split(" to ")
1086 a, b = date.split(" to ")
1087 start, stop = lower(a), upper(b)
1087 start, stop = lower(a), upper(b)
1088 return lambda x: x >= start and x <= stop
1088 return lambda x: x >= start and x <= stop
1089 else:
1089 else:
1090 start, stop = lower(date), upper(date)
1090 start, stop = lower(date), upper(date)
1091 return lambda x: x >= start and x <= stop
1091 return lambda x: x >= start and x <= stop
1092
1092
1093 def shortuser(user):
1093 def shortuser(user):
1094 """Return a short representation of a user name or email address."""
1094 """Return a short representation of a user name or email address."""
1095 f = user.find('@')
1095 f = user.find('@')
1096 if f >= 0:
1096 if f >= 0:
1097 user = user[:f]
1097 user = user[:f]
1098 f = user.find('<')
1098 f = user.find('<')
1099 if f >= 0:
1099 if f >= 0:
1100 user = user[f + 1:]
1100 user = user[f + 1:]
1101 f = user.find(' ')
1101 f = user.find(' ')
1102 if f >= 0:
1102 if f >= 0:
1103 user = user[:f]
1103 user = user[:f]
1104 f = user.find('.')
1104 f = user.find('.')
1105 if f >= 0:
1105 if f >= 0:
1106 user = user[:f]
1106 user = user[:f]
1107 return user
1107 return user
1108
1108
1109 def email(author):
1109 def email(author):
1110 '''get email of author.'''
1110 '''get email of author.'''
1111 r = author.find('>')
1111 r = author.find('>')
1112 if r == -1:
1112 if r == -1:
1113 r = None
1113 r = None
1114 return author[author.find('<') + 1:r]
1114 return author[author.find('<') + 1:r]
1115
1115
1116 def _ellipsis(text, maxlength):
1116 def _ellipsis(text, maxlength):
1117 if len(text) <= maxlength:
1117 if len(text) <= maxlength:
1118 return text, False
1118 return text, False
1119 else:
1119 else:
1120 return "%s..." % (text[:maxlength - 3]), True
1120 return "%s..." % (text[:maxlength - 3]), True
1121
1121
1122 def ellipsis(text, maxlength=400):
1122 def ellipsis(text, maxlength=400):
1123 """Trim string to at most maxlength (default: 400) characters."""
1123 """Trim string to at most maxlength (default: 400) characters."""
1124 try:
1124 try:
1125 # use unicode not to split at intermediate multi-byte sequence
1125 # use unicode not to split at intermediate multi-byte sequence
1126 utext, truncated = _ellipsis(text.decode(encoding.encoding),
1126 utext, truncated = _ellipsis(text.decode(encoding.encoding),
1127 maxlength)
1127 maxlength)
1128 if not truncated:
1128 if not truncated:
1129 return text
1129 return text
1130 return utext.encode(encoding.encoding)
1130 return utext.encode(encoding.encoding)
1131 except (UnicodeDecodeError, UnicodeEncodeError):
1131 except (UnicodeDecodeError, UnicodeEncodeError):
1132 return _ellipsis(text, maxlength)[0]
1132 return _ellipsis(text, maxlength)[0]
1133
1133
1134 def bytecount(nbytes):
1134 def bytecount(nbytes):
1135 '''return byte count formatted as readable string, with units'''
1135 '''return byte count formatted as readable string, with units'''
1136
1136
1137 units = (
1137 units = (
1138 (100, 1 << 30, _('%.0f GB')),
1138 (100, 1 << 30, _('%.0f GB')),
1139 (10, 1 << 30, _('%.1f GB')),
1139 (10, 1 << 30, _('%.1f GB')),
1140 (1, 1 << 30, _('%.2f GB')),
1140 (1, 1 << 30, _('%.2f GB')),
1141 (100, 1 << 20, _('%.0f MB')),
1141 (100, 1 << 20, _('%.0f MB')),
1142 (10, 1 << 20, _('%.1f MB')),
1142 (10, 1 << 20, _('%.1f MB')),
1143 (1, 1 << 20, _('%.2f MB')),
1143 (1, 1 << 20, _('%.2f MB')),
1144 (100, 1 << 10, _('%.0f KB')),
1144 (100, 1 << 10, _('%.0f KB')),
1145 (10, 1 << 10, _('%.1f KB')),
1145 (10, 1 << 10, _('%.1f KB')),
1146 (1, 1 << 10, _('%.2f KB')),
1146 (1, 1 << 10, _('%.2f KB')),
1147 (1, 1, _('%.0f bytes')),
1147 (1, 1, _('%.0f bytes')),
1148 )
1148 )
1149
1149
1150 for multiplier, divisor, format in units:
1150 for multiplier, divisor, format in units:
1151 if nbytes >= divisor * multiplier:
1151 if nbytes >= divisor * multiplier:
1152 return format % (nbytes / float(divisor))
1152 return format % (nbytes / float(divisor))
1153 return units[-1][2] % nbytes
1153 return units[-1][2] % nbytes
1154
1154
1155 def uirepr(s):
1155 def uirepr(s):
1156 # Avoid double backslash in Windows path repr()
1156 # Avoid double backslash in Windows path repr()
1157 return repr(s).replace('\\\\', '\\')
1157 return repr(s).replace('\\\\', '\\')
1158
1158
1159 # delay import of textwrap
1159 # delay import of textwrap
1160 def MBTextWrapper(**kwargs):
1160 def MBTextWrapper(**kwargs):
1161 class tw(textwrap.TextWrapper):
1161 class tw(textwrap.TextWrapper):
1162 """
1162 """
1163 Extend TextWrapper for width-awareness.
1163 Extend TextWrapper for width-awareness.
1164
1164
1165 Neither number of 'bytes' in any encoding nor 'characters' is
1165 Neither number of 'bytes' in any encoding nor 'characters' is
1166 appropriate to calculate terminal columns for specified string.
1166 appropriate to calculate terminal columns for specified string.
1167
1167
1168 Original TextWrapper implementation uses built-in 'len()' directly,
1168 Original TextWrapper implementation uses built-in 'len()' directly,
1169 so overriding is needed to use width information of each characters.
1169 so overriding is needed to use width information of each characters.
1170
1170
1171 In addition, characters classified into 'ambiguous' width are
1171 In addition, characters classified into 'ambiguous' width are
1172 treated as wide in east asian area, but as narrow in other.
1172 treated as wide in east asian area, but as narrow in other.
1173
1173
1174 This requires use decision to determine width of such characters.
1174 This requires use decision to determine width of such characters.
1175 """
1175 """
1176 def __init__(self, **kwargs):
1176 def __init__(self, **kwargs):
1177 textwrap.TextWrapper.__init__(self, **kwargs)
1177 textwrap.TextWrapper.__init__(self, **kwargs)
1178
1178
1179 # for compatibility between 2.4 and 2.6
1179 # for compatibility between 2.4 and 2.6
1180 if getattr(self, 'drop_whitespace', None) is None:
1180 if getattr(self, 'drop_whitespace', None) is None:
1181 self.drop_whitespace = kwargs.get('drop_whitespace', True)
1181 self.drop_whitespace = kwargs.get('drop_whitespace', True)
1182
1182
1183 def _cutdown(self, ucstr, space_left):
1183 def _cutdown(self, ucstr, space_left):
1184 l = 0
1184 l = 0
1185 colwidth = encoding.ucolwidth
1185 colwidth = encoding.ucolwidth
1186 for i in xrange(len(ucstr)):
1186 for i in xrange(len(ucstr)):
1187 l += colwidth(ucstr[i])
1187 l += colwidth(ucstr[i])
1188 if space_left < l:
1188 if space_left < l:
1189 return (ucstr[:i], ucstr[i:])
1189 return (ucstr[:i], ucstr[i:])
1190 return ucstr, ''
1190 return ucstr, ''
1191
1191
1192 # overriding of base class
1192 # overriding of base class
1193 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1193 def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
1194 space_left = max(width - cur_len, 1)
1194 space_left = max(width - cur_len, 1)
1195
1195
1196 if self.break_long_words:
1196 if self.break_long_words:
1197 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1197 cut, res = self._cutdown(reversed_chunks[-1], space_left)
1198 cur_line.append(cut)
1198 cur_line.append(cut)
1199 reversed_chunks[-1] = res
1199 reversed_chunks[-1] = res
1200 elif not cur_line:
1200 elif not cur_line:
1201 cur_line.append(reversed_chunks.pop())
1201 cur_line.append(reversed_chunks.pop())
1202
1202
1203 # this overriding code is imported from TextWrapper of python 2.6
1203 # this overriding code is imported from TextWrapper of python 2.6
1204 # to calculate columns of string by 'encoding.ucolwidth()'
1204 # to calculate columns of string by 'encoding.ucolwidth()'
1205 def _wrap_chunks(self, chunks):
1205 def _wrap_chunks(self, chunks):
1206 colwidth = encoding.ucolwidth
1206 colwidth = encoding.ucolwidth
1207
1207
1208 lines = []
1208 lines = []
1209 if self.width <= 0:
1209 if self.width <= 0:
1210 raise ValueError("invalid width %r (must be > 0)" % self.width)
1210 raise ValueError("invalid width %r (must be > 0)" % self.width)
1211
1211
1212 # Arrange in reverse order so items can be efficiently popped
1212 # Arrange in reverse order so items can be efficiently popped
1213 # from a stack of chucks.
1213 # from a stack of chucks.
1214 chunks.reverse()
1214 chunks.reverse()
1215
1215
1216 while chunks:
1216 while chunks:
1217
1217
1218 # Start the list of chunks that will make up the current line.
1218 # Start the list of chunks that will make up the current line.
1219 # cur_len is just the length of all the chunks in cur_line.
1219 # cur_len is just the length of all the chunks in cur_line.
1220 cur_line = []
1220 cur_line = []
1221 cur_len = 0
1221 cur_len = 0
1222
1222
1223 # Figure out which static string will prefix this line.
1223 # Figure out which static string will prefix this line.
1224 if lines:
1224 if lines:
1225 indent = self.subsequent_indent
1225 indent = self.subsequent_indent
1226 else:
1226 else:
1227 indent = self.initial_indent
1227 indent = self.initial_indent
1228
1228
1229 # Maximum width for this line.
1229 # Maximum width for this line.
1230 width = self.width - len(indent)
1230 width = self.width - len(indent)
1231
1231
1232 # First chunk on line is whitespace -- drop it, unless this
1232 # First chunk on line is whitespace -- drop it, unless this
1233 # is the very beginning of the text (ie. no lines started yet).
1233 # is the very beginning of the text (ie. no lines started yet).
1234 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
1234 if self.drop_whitespace and chunks[-1].strip() == '' and lines:
1235 del chunks[-1]
1235 del chunks[-1]
1236
1236
1237 while chunks:
1237 while chunks:
1238 l = colwidth(chunks[-1])
1238 l = colwidth(chunks[-1])
1239
1239
1240 # Can at least squeeze this chunk onto the current line.
1240 # Can at least squeeze this chunk onto the current line.
1241 if cur_len + l <= width:
1241 if cur_len + l <= width:
1242 cur_line.append(chunks.pop())
1242 cur_line.append(chunks.pop())
1243 cur_len += l
1243 cur_len += l
1244
1244
1245 # Nope, this line is full.
1245 # Nope, this line is full.
1246 else:
1246 else:
1247 break
1247 break
1248
1248
1249 # The current line is full, and the next chunk is too big to
1249 # The current line is full, and the next chunk is too big to
1250 # fit on *any* line (not just this one).
1250 # fit on *any* line (not just this one).
1251 if chunks and colwidth(chunks[-1]) > width:
1251 if chunks and colwidth(chunks[-1]) > width:
1252 self._handle_long_word(chunks, cur_line, cur_len, width)
1252 self._handle_long_word(chunks, cur_line, cur_len, width)
1253
1253
1254 # If the last chunk on this line is all whitespace, drop it.
1254 # If the last chunk on this line is all whitespace, drop it.
1255 if (self.drop_whitespace and
1255 if (self.drop_whitespace and
1256 cur_line and cur_line[-1].strip() == ''):
1256 cur_line and cur_line[-1].strip() == ''):
1257 del cur_line[-1]
1257 del cur_line[-1]
1258
1258
1259 # Convert current line back to a string and store it in list
1259 # Convert current line back to a string and store it in list
1260 # of all lines (return value).
1260 # of all lines (return value).
1261 if cur_line:
1261 if cur_line:
1262 lines.append(indent + ''.join(cur_line))
1262 lines.append(indent + ''.join(cur_line))
1263
1263
1264 return lines
1264 return lines
1265
1265
1266 global MBTextWrapper
1266 global MBTextWrapper
1267 MBTextWrapper = tw
1267 MBTextWrapper = tw
1268 return tw(**kwargs)
1268 return tw(**kwargs)
1269
1269
1270 def wrap(line, width, initindent='', hangindent=''):
1270 def wrap(line, width, initindent='', hangindent=''):
1271 maxindent = max(len(hangindent), len(initindent))
1271 maxindent = max(len(hangindent), len(initindent))
1272 if width <= maxindent:
1272 if width <= maxindent:
1273 # adjust for weird terminal size
1273 # adjust for weird terminal size
1274 width = max(78, maxindent + 1)
1274 width = max(78, maxindent + 1)
1275 line = line.decode(encoding.encoding, encoding.encodingmode)
1275 line = line.decode(encoding.encoding, encoding.encodingmode)
1276 initindent = initindent.decode(encoding.encoding, encoding.encodingmode)
1276 initindent = initindent.decode(encoding.encoding, encoding.encodingmode)
1277 hangindent = hangindent.decode(encoding.encoding, encoding.encodingmode)
1277 hangindent = hangindent.decode(encoding.encoding, encoding.encodingmode)
1278 wrapper = MBTextWrapper(width=width,
1278 wrapper = MBTextWrapper(width=width,
1279 initial_indent=initindent,
1279 initial_indent=initindent,
1280 subsequent_indent=hangindent)
1280 subsequent_indent=hangindent)
1281 return wrapper.fill(line).encode(encoding.encoding)
1281 return wrapper.fill(line).encode(encoding.encoding)
1282
1282
1283 def iterlines(iterator):
1283 def iterlines(iterator):
1284 for chunk in iterator:
1284 for chunk in iterator:
1285 for line in chunk.splitlines():
1285 for line in chunk.splitlines():
1286 yield line
1286 yield line
1287
1287
1288 def expandpath(path):
1288 def expandpath(path):
1289 return os.path.expanduser(os.path.expandvars(path))
1289 return os.path.expanduser(os.path.expandvars(path))
1290
1290
1291 def hgcmd():
1291 def hgcmd():
1292 """Return the command used to execute current hg
1292 """Return the command used to execute current hg
1293
1293
1294 This is different from hgexecutable() because on Windows we want
1294 This is different from hgexecutable() because on Windows we want
1295 to avoid things opening new shell windows like batch files, so we
1295 to avoid things opening new shell windows like batch files, so we
1296 get either the python call or current executable.
1296 get either the python call or current executable.
1297 """
1297 """
1298 if mainfrozen():
1298 if mainfrozen():
1299 return [sys.executable]
1299 return [sys.executable]
1300 return gethgcmd()
1300 return gethgcmd()
1301
1301
1302 def rundetached(args, condfn):
1302 def rundetached(args, condfn):
1303 """Execute the argument list in a detached process.
1303 """Execute the argument list in a detached process.
1304
1304
1305 condfn is a callable which is called repeatedly and should return
1305 condfn is a callable which is called repeatedly and should return
1306 True once the child process is known to have started successfully.
1306 True once the child process is known to have started successfully.
1307 At this point, the child process PID is returned. If the child
1307 At this point, the child process PID is returned. If the child
1308 process fails to start or finishes before condfn() evaluates to
1308 process fails to start or finishes before condfn() evaluates to
1309 True, return -1.
1309 True, return -1.
1310 """
1310 """
1311 # Windows case is easier because the child process is either
1311 # Windows case is easier because the child process is either
1312 # successfully starting and validating the condition or exiting
1312 # successfully starting and validating the condition or exiting
1313 # on failure. We just poll on its PID. On Unix, if the child
1313 # on failure. We just poll on its PID. On Unix, if the child
1314 # process fails to start, it will be left in a zombie state until
1314 # process fails to start, it will be left in a zombie state until
1315 # the parent wait on it, which we cannot do since we expect a long
1315 # the parent wait on it, which we cannot do since we expect a long
1316 # running process on success. Instead we listen for SIGCHLD telling
1316 # running process on success. Instead we listen for SIGCHLD telling
1317 # us our child process terminated.
1317 # us our child process terminated.
1318 terminated = set()
1318 terminated = set()
1319 def handler(signum, frame):
1319 def handler(signum, frame):
1320 terminated.add(os.wait())
1320 terminated.add(os.wait())
1321 prevhandler = None
1321 prevhandler = None
1322 SIGCHLD = getattr(signal, 'SIGCHLD', None)
1322 SIGCHLD = getattr(signal, 'SIGCHLD', None)
1323 if SIGCHLD is not None:
1323 if SIGCHLD is not None:
1324 prevhandler = signal.signal(SIGCHLD, handler)
1324 prevhandler = signal.signal(SIGCHLD, handler)
1325 try:
1325 try:
1326 pid = spawndetached(args)
1326 pid = spawndetached(args)
1327 while not condfn():
1327 while not condfn():
1328 if ((pid in terminated or not testpid(pid))
1328 if ((pid in terminated or not testpid(pid))
1329 and not condfn()):
1329 and not condfn()):
1330 return -1
1330 return -1
1331 time.sleep(0.1)
1331 time.sleep(0.1)
1332 return pid
1332 return pid
1333 finally:
1333 finally:
1334 if prevhandler is not None:
1334 if prevhandler is not None:
1335 signal.signal(signal.SIGCHLD, prevhandler)
1335 signal.signal(signal.SIGCHLD, prevhandler)
1336
1336
1337 try:
1337 try:
1338 any, all = any, all
1338 any, all = any, all
1339 except NameError:
1339 except NameError:
1340 def any(iterable):
1340 def any(iterable):
1341 for i in iterable:
1341 for i in iterable:
1342 if i:
1342 if i:
1343 return True
1343 return True
1344 return False
1344 return False
1345
1345
1346 def all(iterable):
1346 def all(iterable):
1347 for i in iterable:
1347 for i in iterable:
1348 if not i:
1348 if not i:
1349 return False
1349 return False
1350 return True
1350 return True
1351
1351
1352 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1352 def interpolate(prefix, mapping, s, fn=None, escape_prefix=False):
1353 """Return the result of interpolating items in the mapping into string s.
1353 """Return the result of interpolating items in the mapping into string s.
1354
1354
1355 prefix is a single character string, or a two character string with
1355 prefix is a single character string, or a two character string with
1356 a backslash as the first character if the prefix needs to be escaped in
1356 a backslash as the first character if the prefix needs to be escaped in
1357 a regular expression.
1357 a regular expression.
1358
1358
1359 fn is an optional function that will be applied to the replacement text
1359 fn is an optional function that will be applied to the replacement text
1360 just before replacement.
1360 just before replacement.
1361
1361
1362 escape_prefix is an optional flag that allows using doubled prefix for
1362 escape_prefix is an optional flag that allows using doubled prefix for
1363 its escaping.
1363 its escaping.
1364 """
1364 """
1365 fn = fn or (lambda s: s)
1365 fn = fn or (lambda s: s)
1366 patterns = '|'.join(mapping.keys())
1366 patterns = '|'.join(mapping.keys())
1367 if escape_prefix:
1367 if escape_prefix:
1368 patterns += '|' + prefix
1368 patterns += '|' + prefix
1369 if len(prefix) > 1:
1369 if len(prefix) > 1:
1370 prefix_char = prefix[1:]
1370 prefix_char = prefix[1:]
1371 else:
1371 else:
1372 prefix_char = prefix
1372 prefix_char = prefix
1373 mapping[prefix_char] = prefix_char
1373 mapping[prefix_char] = prefix_char
1374 r = re.compile(r'%s(%s)' % (prefix, patterns))
1374 r = re.compile(r'%s(%s)' % (prefix, patterns))
1375 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1375 return r.sub(lambda x: fn(mapping[x.group()[1:]]), s)
1376
1376
1377 def getport(port):
1377 def getport(port):
1378 """Return the port for a given network service.
1378 """Return the port for a given network service.
1379
1379
1380 If port is an integer, it's returned as is. If it's a string, it's
1380 If port is an integer, it's returned as is. If it's a string, it's
1381 looked up using socket.getservbyname(). If there's no matching
1381 looked up using socket.getservbyname(). If there's no matching
1382 service, util.Abort is raised.
1382 service, util.Abort is raised.
1383 """
1383 """
1384 try:
1384 try:
1385 return int(port)
1385 return int(port)
1386 except ValueError:
1386 except ValueError:
1387 pass
1387 pass
1388
1388
1389 try:
1389 try:
1390 return socket.getservbyname(port)
1390 return socket.getservbyname(port)
1391 except socket.error:
1391 except socket.error:
1392 raise Abort(_("no port number associated with service '%s'") % port)
1392 raise Abort(_("no port number associated with service '%s'") % port)
1393
1393
1394 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1394 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True, 'always': True,
1395 '0': False, 'no': False, 'false': False, 'off': False,
1395 '0': False, 'no': False, 'false': False, 'off': False,
1396 'never': False}
1396 'never': False}
1397
1397
1398 def parsebool(s):
1398 def parsebool(s):
1399 """Parse s into a boolean.
1399 """Parse s into a boolean.
1400
1400
1401 If s is not a valid boolean, returns None.
1401 If s is not a valid boolean, returns None.
1402 """
1402 """
1403 return _booleans.get(s.lower(), None)
1403 return _booleans.get(s.lower(), None)
1404
1404
1405 _hexdig = '0123456789ABCDEFabcdef'
1405 _hexdig = '0123456789ABCDEFabcdef'
1406 _hextochr = dict((a + b, chr(int(a + b, 16)))
1406 _hextochr = dict((a + b, chr(int(a + b, 16)))
1407 for a in _hexdig for b in _hexdig)
1407 for a in _hexdig for b in _hexdig)
1408
1408
1409 def _urlunquote(s):
1409 def _urlunquote(s):
1410 """unquote('abc%20def') -> 'abc def'."""
1410 """unquote('abc%20def') -> 'abc def'."""
1411 res = s.split('%')
1411 res = s.split('%')
1412 # fastpath
1412 # fastpath
1413 if len(res) == 1:
1413 if len(res) == 1:
1414 return s
1414 return s
1415 s = res[0]
1415 s = res[0]
1416 for item in res[1:]:
1416 for item in res[1:]:
1417 try:
1417 try:
1418 s += _hextochr[item[:2]] + item[2:]
1418 s += _hextochr[item[:2]] + item[2:]
1419 except KeyError:
1419 except KeyError:
1420 s += '%' + item
1420 s += '%' + item
1421 except UnicodeDecodeError:
1421 except UnicodeDecodeError:
1422 s += unichr(int(item[:2], 16)) + item[2:]
1422 s += unichr(int(item[:2], 16)) + item[2:]
1423 return s
1423 return s
1424
1424
1425 class url(object):
1425 class url(object):
1426 r"""Reliable URL parser.
1426 r"""Reliable URL parser.
1427
1427
1428 This parses URLs and provides attributes for the following
1428 This parses URLs and provides attributes for the following
1429 components:
1429 components:
1430
1430
1431 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
1431 <scheme>://<user>:<passwd>@<host>:<port>/<path>?<query>#<fragment>
1432
1432
1433 Missing components are set to None. The only exception is
1433 Missing components are set to None. The only exception is
1434 fragment, which is set to '' if present but empty.
1434 fragment, which is set to '' if present but empty.
1435
1435
1436 If parsefragment is False, fragment is included in query. If
1436 If parsefragment is False, fragment is included in query. If
1437 parsequery is False, query is included in path. If both are
1437 parsequery is False, query is included in path. If both are
1438 False, both fragment and query are included in path.
1438 False, both fragment and query are included in path.
1439
1439
1440 See http://www.ietf.org/rfc/rfc2396.txt for more information.
1440 See http://www.ietf.org/rfc/rfc2396.txt for more information.
1441
1441
1442 Note that for backward compatibility reasons, bundle URLs do not
1442 Note that for backward compatibility reasons, bundle URLs do not
1443 take host names. That means 'bundle://../' has a path of '../'.
1443 take host names. That means 'bundle://../' has a path of '../'.
1444
1444
1445 Examples:
1445 Examples:
1446
1446
1447 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
1447 >>> url('http://www.ietf.org/rfc/rfc2396.txt')
1448 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
1448 <url scheme: 'http', host: 'www.ietf.org', path: 'rfc/rfc2396.txt'>
1449 >>> url('ssh://[::1]:2200//home/joe/repo')
1449 >>> url('ssh://[::1]:2200//home/joe/repo')
1450 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
1450 <url scheme: 'ssh', host: '[::1]', port: '2200', path: '/home/joe/repo'>
1451 >>> url('file:///home/joe/repo')
1451 >>> url('file:///home/joe/repo')
1452 <url scheme: 'file', path: '/home/joe/repo'>
1452 <url scheme: 'file', path: '/home/joe/repo'>
1453 >>> url('file:///c:/temp/foo/')
1453 >>> url('file:///c:/temp/foo/')
1454 <url scheme: 'file', path: 'c:/temp/foo/'>
1454 <url scheme: 'file', path: 'c:/temp/foo/'>
1455 >>> url('bundle:foo')
1455 >>> url('bundle:foo')
1456 <url scheme: 'bundle', path: 'foo'>
1456 <url scheme: 'bundle', path: 'foo'>
1457 >>> url('bundle://../foo')
1457 >>> url('bundle://../foo')
1458 <url scheme: 'bundle', path: '../foo'>
1458 <url scheme: 'bundle', path: '../foo'>
1459 >>> url(r'c:\foo\bar')
1459 >>> url(r'c:\foo\bar')
1460 <url path: 'c:\\foo\\bar'>
1460 <url path: 'c:\\foo\\bar'>
1461 >>> url(r'\\blah\blah\blah')
1461 >>> url(r'\\blah\blah\blah')
1462 <url path: '\\\\blah\\blah\\blah'>
1462 <url path: '\\\\blah\\blah\\blah'>
1463 >>> url(r'\\blah\blah\blah#baz')
1464 <url path: '\\\\blah\\blah\\blah', fragment: 'baz'>
1463
1465
1464 Authentication credentials:
1466 Authentication credentials:
1465
1467
1466 >>> url('ssh://joe:xyz@x/repo')
1468 >>> url('ssh://joe:xyz@x/repo')
1467 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
1469 <url scheme: 'ssh', user: 'joe', passwd: 'xyz', host: 'x', path: 'repo'>
1468 >>> url('ssh://joe@x/repo')
1470 >>> url('ssh://joe@x/repo')
1469 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
1471 <url scheme: 'ssh', user: 'joe', host: 'x', path: 'repo'>
1470
1472
1471 Query strings and fragments:
1473 Query strings and fragments:
1472
1474
1473 >>> url('http://host/a?b#c')
1475 >>> url('http://host/a?b#c')
1474 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
1476 <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'>
1475 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
1477 >>> url('http://host/a?b#c', parsequery=False, parsefragment=False)
1476 <url scheme: 'http', host: 'host', path: 'a?b#c'>
1478 <url scheme: 'http', host: 'host', path: 'a?b#c'>
1477 """
1479 """
1478
1480
1479 _safechars = "!~*'()+"
1481 _safechars = "!~*'()+"
1480 _safepchars = "/!~*'()+"
1482 _safepchars = "/!~*'()+"
1481 _matchscheme = re.compile(r'^[a-zA-Z0-9+.\-]+:').match
1483 _matchscheme = re.compile(r'^[a-zA-Z0-9+.\-]+:').match
1482
1484
1483 def __init__(self, path, parsequery=True, parsefragment=True):
1485 def __init__(self, path, parsequery=True, parsefragment=True):
1484 # We slowly chomp away at path until we have only the path left
1486 # We slowly chomp away at path until we have only the path left
1485 self.scheme = self.user = self.passwd = self.host = None
1487 self.scheme = self.user = self.passwd = self.host = None
1486 self.port = self.path = self.query = self.fragment = None
1488 self.port = self.path = self.query = self.fragment = None
1487 self._localpath = True
1489 self._localpath = True
1488 self._hostport = ''
1490 self._hostport = ''
1489 self._origpath = path
1491 self._origpath = path
1490
1492
1493 if parsefragment and '#' in path:
1494 path, self.fragment = path.split('#', 1)
1495 if not path:
1496 path = None
1497
1491 # special case for Windows drive letters and UNC paths
1498 # special case for Windows drive letters and UNC paths
1492 if hasdriveletter(path) or path.startswith(r'\\'):
1499 if hasdriveletter(path) or path.startswith(r'\\'):
1493 self.path = path
1500 self.path = path
1494 return
1501 return
1495
1502
1496 # For compatibility reasons, we can't handle bundle paths as
1503 # For compatibility reasons, we can't handle bundle paths as
1497 # normal URLS
1504 # normal URLS
1498 if path.startswith('bundle:'):
1505 if path.startswith('bundle:'):
1499 self.scheme = 'bundle'
1506 self.scheme = 'bundle'
1500 path = path[7:]
1507 path = path[7:]
1501 if path.startswith('//'):
1508 if path.startswith('//'):
1502 path = path[2:]
1509 path = path[2:]
1503 self.path = path
1510 self.path = path
1504 return
1511 return
1505
1512
1506 if self._matchscheme(path):
1513 if self._matchscheme(path):
1507 parts = path.split(':', 1)
1514 parts = path.split(':', 1)
1508 if parts[0]:
1515 if parts[0]:
1509 self.scheme, path = parts
1516 self.scheme, path = parts
1510 self._localpath = False
1517 self._localpath = False
1511
1518
1512 if not path:
1519 if not path:
1513 path = None
1520 path = None
1514 if self._localpath:
1521 if self._localpath:
1515 self.path = ''
1522 self.path = ''
1516 return
1523 return
1517 else:
1524 else:
1518 if parsefragment and '#' in path:
1519 path, self.fragment = path.split('#', 1)
1520 if not path:
1521 path = None
1522 if self._localpath:
1525 if self._localpath:
1523 self.path = path
1526 self.path = path
1524 return
1527 return
1525
1528
1526 if parsequery and '?' in path:
1529 if parsequery and '?' in path:
1527 path, self.query = path.split('?', 1)
1530 path, self.query = path.split('?', 1)
1528 if not path:
1531 if not path:
1529 path = None
1532 path = None
1530 if not self.query:
1533 if not self.query:
1531 self.query = None
1534 self.query = None
1532
1535
1533 # // is required to specify a host/authority
1536 # // is required to specify a host/authority
1534 if path and path.startswith('//'):
1537 if path and path.startswith('//'):
1535 parts = path[2:].split('/', 1)
1538 parts = path[2:].split('/', 1)
1536 if len(parts) > 1:
1539 if len(parts) > 1:
1537 self.host, path = parts
1540 self.host, path = parts
1538 path = path
1541 path = path
1539 else:
1542 else:
1540 self.host = parts[0]
1543 self.host = parts[0]
1541 path = None
1544 path = None
1542 if not self.host:
1545 if not self.host:
1543 self.host = None
1546 self.host = None
1544 # path of file:///d is /d
1547 # path of file:///d is /d
1545 # path of file:///d:/ is d:/, not /d:/
1548 # path of file:///d:/ is d:/, not /d:/
1546 if path and not hasdriveletter(path):
1549 if path and not hasdriveletter(path):
1547 path = '/' + path
1550 path = '/' + path
1548
1551
1549 if self.host and '@' in self.host:
1552 if self.host and '@' in self.host:
1550 self.user, self.host = self.host.rsplit('@', 1)
1553 self.user, self.host = self.host.rsplit('@', 1)
1551 if ':' in self.user:
1554 if ':' in self.user:
1552 self.user, self.passwd = self.user.split(':', 1)
1555 self.user, self.passwd = self.user.split(':', 1)
1553 if not self.host:
1556 if not self.host:
1554 self.host = None
1557 self.host = None
1555
1558
1556 # Don't split on colons in IPv6 addresses without ports
1559 # Don't split on colons in IPv6 addresses without ports
1557 if (self.host and ':' in self.host and
1560 if (self.host and ':' in self.host and
1558 not (self.host.startswith('[') and self.host.endswith(']'))):
1561 not (self.host.startswith('[') and self.host.endswith(']'))):
1559 self._hostport = self.host
1562 self._hostport = self.host
1560 self.host, self.port = self.host.rsplit(':', 1)
1563 self.host, self.port = self.host.rsplit(':', 1)
1561 if not self.host:
1564 if not self.host:
1562 self.host = None
1565 self.host = None
1563
1566
1564 if (self.host and self.scheme == 'file' and
1567 if (self.host and self.scheme == 'file' and
1565 self.host not in ('localhost', '127.0.0.1', '[::1]')):
1568 self.host not in ('localhost', '127.0.0.1', '[::1]')):
1566 raise Abort(_('file:// URLs can only refer to localhost'))
1569 raise Abort(_('file:// URLs can only refer to localhost'))
1567
1570
1568 self.path = path
1571 self.path = path
1569
1572
1570 # leave the query string escaped
1573 # leave the query string escaped
1571 for a in ('user', 'passwd', 'host', 'port',
1574 for a in ('user', 'passwd', 'host', 'port',
1572 'path', 'fragment'):
1575 'path', 'fragment'):
1573 v = getattr(self, a)
1576 v = getattr(self, a)
1574 if v is not None:
1577 if v is not None:
1575 setattr(self, a, _urlunquote(v))
1578 setattr(self, a, _urlunquote(v))
1576
1579
1577 def __repr__(self):
1580 def __repr__(self):
1578 attrs = []
1581 attrs = []
1579 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
1582 for a in ('scheme', 'user', 'passwd', 'host', 'port', 'path',
1580 'query', 'fragment'):
1583 'query', 'fragment'):
1581 v = getattr(self, a)
1584 v = getattr(self, a)
1582 if v is not None:
1585 if v is not None:
1583 attrs.append('%s: %r' % (a, v))
1586 attrs.append('%s: %r' % (a, v))
1584 return '<url %s>' % ', '.join(attrs)
1587 return '<url %s>' % ', '.join(attrs)
1585
1588
1586 def __str__(self):
1589 def __str__(self):
1587 r"""Join the URL's components back into a URL string.
1590 r"""Join the URL's components back into a URL string.
1588
1591
1589 Examples:
1592 Examples:
1590
1593
1591 >>> str(url('http://user:pw@host:80/?foo#bar'))
1594 >>> str(url('http://user:pw@host:80/?foo#bar'))
1592 'http://user:pw@host:80/?foo#bar'
1595 'http://user:pw@host:80/?foo#bar'
1593 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
1596 >>> str(url('http://user:pw@host:80/?foo=bar&baz=42'))
1594 'http://user:pw@host:80/?foo=bar&baz=42'
1597 'http://user:pw@host:80/?foo=bar&baz=42'
1595 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
1598 >>> str(url('http://user:pw@host:80/?foo=bar%3dbaz'))
1596 'http://user:pw@host:80/?foo=bar%3dbaz'
1599 'http://user:pw@host:80/?foo=bar%3dbaz'
1597 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
1600 >>> str(url('ssh://user:pw@[::1]:2200//home/joe#'))
1598 'ssh://user:pw@[::1]:2200//home/joe#'
1601 'ssh://user:pw@[::1]:2200//home/joe#'
1599 >>> str(url('http://localhost:80//'))
1602 >>> str(url('http://localhost:80//'))
1600 'http://localhost:80//'
1603 'http://localhost:80//'
1601 >>> str(url('http://localhost:80/'))
1604 >>> str(url('http://localhost:80/'))
1602 'http://localhost:80/'
1605 'http://localhost:80/'
1603 >>> str(url('http://localhost:80'))
1606 >>> str(url('http://localhost:80'))
1604 'http://localhost:80/'
1607 'http://localhost:80/'
1605 >>> str(url('bundle:foo'))
1608 >>> str(url('bundle:foo'))
1606 'bundle:foo'
1609 'bundle:foo'
1607 >>> str(url('bundle://../foo'))
1610 >>> str(url('bundle://../foo'))
1608 'bundle:../foo'
1611 'bundle:../foo'
1609 >>> str(url('path'))
1612 >>> str(url('path'))
1610 'path'
1613 'path'
1611 >>> str(url('file:///tmp/foo/bar'))
1614 >>> str(url('file:///tmp/foo/bar'))
1612 'file:///tmp/foo/bar'
1615 'file:///tmp/foo/bar'
1613 >>> print url(r'bundle:foo\bar')
1616 >>> print url(r'bundle:foo\bar')
1614 bundle:foo\bar
1617 bundle:foo\bar
1615 """
1618 """
1616 if self._localpath:
1619 if self._localpath:
1617 s = self.path
1620 s = self.path
1618 if self.scheme == 'bundle':
1621 if self.scheme == 'bundle':
1619 s = 'bundle:' + s
1622 s = 'bundle:' + s
1620 if self.fragment:
1623 if self.fragment:
1621 s += '#' + self.fragment
1624 s += '#' + self.fragment
1622 return s
1625 return s
1623
1626
1624 s = self.scheme + ':'
1627 s = self.scheme + ':'
1625 if self.user or self.passwd or self.host:
1628 if self.user or self.passwd or self.host:
1626 s += '//'
1629 s += '//'
1627 elif self.scheme and (not self.path or self.path.startswith('/')):
1630 elif self.scheme and (not self.path or self.path.startswith('/')):
1628 s += '//'
1631 s += '//'
1629 if self.user:
1632 if self.user:
1630 s += urllib.quote(self.user, safe=self._safechars)
1633 s += urllib.quote(self.user, safe=self._safechars)
1631 if self.passwd:
1634 if self.passwd:
1632 s += ':' + urllib.quote(self.passwd, safe=self._safechars)
1635 s += ':' + urllib.quote(self.passwd, safe=self._safechars)
1633 if self.user or self.passwd:
1636 if self.user or self.passwd:
1634 s += '@'
1637 s += '@'
1635 if self.host:
1638 if self.host:
1636 if not (self.host.startswith('[') and self.host.endswith(']')):
1639 if not (self.host.startswith('[') and self.host.endswith(']')):
1637 s += urllib.quote(self.host)
1640 s += urllib.quote(self.host)
1638 else:
1641 else:
1639 s += self.host
1642 s += self.host
1640 if self.port:
1643 if self.port:
1641 s += ':' + urllib.quote(self.port)
1644 s += ':' + urllib.quote(self.port)
1642 if self.host:
1645 if self.host:
1643 s += '/'
1646 s += '/'
1644 if self.path:
1647 if self.path:
1645 # TODO: similar to the query string, we should not unescape the
1648 # TODO: similar to the query string, we should not unescape the
1646 # path when we store it, the path might contain '%2f' = '/',
1649 # path when we store it, the path might contain '%2f' = '/',
1647 # which we should *not* escape.
1650 # which we should *not* escape.
1648 s += urllib.quote(self.path, safe=self._safepchars)
1651 s += urllib.quote(self.path, safe=self._safepchars)
1649 if self.query:
1652 if self.query:
1650 # we store the query in escaped form.
1653 # we store the query in escaped form.
1651 s += '?' + self.query
1654 s += '?' + self.query
1652 if self.fragment is not None:
1655 if self.fragment is not None:
1653 s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
1656 s += '#' + urllib.quote(self.fragment, safe=self._safepchars)
1654 return s
1657 return s
1655
1658
1656 def authinfo(self):
1659 def authinfo(self):
1657 user, passwd = self.user, self.passwd
1660 user, passwd = self.user, self.passwd
1658 try:
1661 try:
1659 self.user, self.passwd = None, None
1662 self.user, self.passwd = None, None
1660 s = str(self)
1663 s = str(self)
1661 finally:
1664 finally:
1662 self.user, self.passwd = user, passwd
1665 self.user, self.passwd = user, passwd
1663 if not self.user:
1666 if not self.user:
1664 return (s, None)
1667 return (s, None)
1665 # authinfo[1] is passed to urllib2 password manager, and its
1668 # authinfo[1] is passed to urllib2 password manager, and its
1666 # URIs must not contain credentials. The host is passed in the
1669 # URIs must not contain credentials. The host is passed in the
1667 # URIs list because Python < 2.4.3 uses only that to search for
1670 # URIs list because Python < 2.4.3 uses only that to search for
1668 # a password.
1671 # a password.
1669 return (s, (None, (s, self.host),
1672 return (s, (None, (s, self.host),
1670 self.user, self.passwd or ''))
1673 self.user, self.passwd or ''))
1671
1674
1672 def isabs(self):
1675 def isabs(self):
1673 if self.scheme and self.scheme != 'file':
1676 if self.scheme and self.scheme != 'file':
1674 return True # remote URL
1677 return True # remote URL
1675 if hasdriveletter(self.path):
1678 if hasdriveletter(self.path):
1676 return True # absolute for our purposes - can't be joined()
1679 return True # absolute for our purposes - can't be joined()
1677 if self.path.startswith(r'\\'):
1680 if self.path.startswith(r'\\'):
1678 return True # Windows UNC path
1681 return True # Windows UNC path
1679 if self.path.startswith('/'):
1682 if self.path.startswith('/'):
1680 return True # POSIX-style
1683 return True # POSIX-style
1681 return False
1684 return False
1682
1685
1683 def localpath(self):
1686 def localpath(self):
1684 if self.scheme == 'file' or self.scheme == 'bundle':
1687 if self.scheme == 'file' or self.scheme == 'bundle':
1685 path = self.path or '/'
1688 path = self.path or '/'
1686 # For Windows, we need to promote hosts containing drive
1689 # For Windows, we need to promote hosts containing drive
1687 # letters to paths with drive letters.
1690 # letters to paths with drive letters.
1688 if hasdriveletter(self._hostport):
1691 if hasdriveletter(self._hostport):
1689 path = self._hostport + '/' + self.path
1692 path = self._hostport + '/' + self.path
1690 elif self.host is not None and self.path:
1693 elif self.host is not None and self.path:
1691 path = '/' + path
1694 path = '/' + path
1692 return path
1695 return path
1693 return self._origpath
1696 return self._origpath
1694
1697
1695 def hasscheme(path):
1698 def hasscheme(path):
1696 return bool(url(path).scheme)
1699 return bool(url(path).scheme)
1697
1700
1698 def hasdriveletter(path):
1701 def hasdriveletter(path):
1699 return path[1:2] == ':' and path[0:1].isalpha()
1702 return path[1:2] == ':' and path[0:1].isalpha()
1700
1703
1701 def urllocalpath(path):
1704 def urllocalpath(path):
1702 return url(path, parsequery=False, parsefragment=False).localpath()
1705 return url(path, parsequery=False, parsefragment=False).localpath()
1703
1706
1704 def hidepassword(u):
1707 def hidepassword(u):
1705 '''hide user credential in a url string'''
1708 '''hide user credential in a url string'''
1706 u = url(u)
1709 u = url(u)
1707 if u.passwd:
1710 if u.passwd:
1708 u.passwd = '***'
1711 u.passwd = '***'
1709 return str(u)
1712 return str(u)
1710
1713
1711 def removeauth(u):
1714 def removeauth(u):
1712 '''remove all authentication information from a url string'''
1715 '''remove all authentication information from a url string'''
1713 u = url(u)
1716 u = url(u)
1714 u.user = u.passwd = None
1717 u.user = u.passwd = None
1715 return str(u)
1718 return str(u)
1716
1719
1717 def isatty(fd):
1720 def isatty(fd):
1718 try:
1721 try:
1719 return fd.isatty()
1722 return fd.isatty()
1720 except AttributeError:
1723 except AttributeError:
1721 return False
1724 return False
@@ -1,1079 +1,1083 b''
1 $ cat <<EOF >> $HGRCPATH
1 $ cat <<EOF >> $HGRCPATH
2 > [extensions]
2 > [extensions]
3 > keyword =
3 > keyword =
4 > mq =
4 > mq =
5 > notify =
5 > notify =
6 > record =
6 > record =
7 > transplant =
7 > transplant =
8 > [ui]
8 > [ui]
9 > interactive = true
9 > interactive = true
10 > EOF
10 > EOF
11
11
12 Run kwdemo before [keyword] files are set up
12 Run kwdemo before [keyword] files are set up
13 as it would succeed without uisetup otherwise
13 as it would succeed without uisetup otherwise
14
14
15 $ hg --quiet kwdemo
15 $ hg --quiet kwdemo
16 [extensions]
16 [extensions]
17 keyword =
17 keyword =
18 [keyword]
18 [keyword]
19 demo.txt =
19 demo.txt =
20 [keywordset]
20 [keywordset]
21 svn = False
21 svn = False
22 [keywordmaps]
22 [keywordmaps]
23 Author = {author|user}
23 Author = {author|user}
24 Date = {date|utcdate}
24 Date = {date|utcdate}
25 Header = {root}/{file},v {node|short} {date|utcdate} {author|user}
25 Header = {root}/{file},v {node|short} {date|utcdate} {author|user}
26 Id = {file|basename},v {node|short} {date|utcdate} {author|user}
26 Id = {file|basename},v {node|short} {date|utcdate} {author|user}
27 RCSFile = {file|basename},v
27 RCSFile = {file|basename},v
28 RCSfile = {file|basename},v
28 RCSfile = {file|basename},v
29 Revision = {node|short}
29 Revision = {node|short}
30 Source = {root}/{file},v
30 Source = {root}/{file},v
31 $Author: test $
31 $Author: test $
32 $Date: ????/??/?? ??:??:?? $ (glob)
32 $Date: ????/??/?? ??:??:?? $ (glob)
33 $Header: */demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
33 $Header: */demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
34 $Id: demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
34 $Id: demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
35 $RCSFile: demo.txt,v $
35 $RCSFile: demo.txt,v $
36 $RCSfile: demo.txt,v $
36 $RCSfile: demo.txt,v $
37 $Revision: ???????????? $ (glob)
37 $Revision: ???????????? $ (glob)
38 $Source: */demo.txt,v $ (glob)
38 $Source: */demo.txt,v $ (glob)
39
39
40 $ hg --quiet kwdemo "Branch = {branches}"
40 $ hg --quiet kwdemo "Branch = {branches}"
41 [extensions]
41 [extensions]
42 keyword =
42 keyword =
43 [keyword]
43 [keyword]
44 demo.txt =
44 demo.txt =
45 [keywordset]
45 [keywordset]
46 svn = False
46 svn = False
47 [keywordmaps]
47 [keywordmaps]
48 Branch = {branches}
48 Branch = {branches}
49 $Branch: demobranch $
49 $Branch: demobranch $
50
50
51 $ cat <<EOF >> $HGRCPATH
51 $ cat <<EOF >> $HGRCPATH
52 > [keyword]
52 > [keyword]
53 > ** =
53 > ** =
54 > b = ignore
54 > b = ignore
55 > i = ignore
55 > i = ignore
56 > [hooks]
56 > [hooks]
57 > EOF
57 > EOF
58 $ cp $HGRCPATH $HGRCPATH.nohooks
58 $ cp $HGRCPATH $HGRCPATH.nohooks
59 > cat <<EOF >> $HGRCPATH
59 > cat <<EOF >> $HGRCPATH
60 > commit=
60 > commit=
61 > commit.test=cp a hooktest
61 > commit.test=cp a hooktest
62 > EOF
62 > EOF
63
63
64 $ hg init Test-bndl
64 $ hg init Test-bndl
65 $ cd Test-bndl
65 $ cd Test-bndl
66
66
67 kwshrink should exit silently in empty/invalid repo
67 kwshrink should exit silently in empty/invalid repo
68
68
69 $ hg kwshrink
69 $ hg kwshrink
70
70
71 Symlinks cannot be created on Windows.
71 Symlinks cannot be created on Windows.
72 A bundle to test this was made with:
72 A bundle to test this was made with:
73 hg init t
73 hg init t
74 cd t
74 cd t
75 echo a > a
75 echo a > a
76 ln -s a sym
76 ln -s a sym
77 hg add sym
77 hg add sym
78 hg ci -m addsym -u mercurial
78 hg ci -m addsym -u mercurial
79 hg bundle --base null ../test-keyword.hg
79 hg bundle --base null ../test-keyword.hg
80
80
81 $ hg pull -u "$TESTDIR"/bundles/test-keyword.hg
81 $ hg pull -u "$TESTDIR"/bundles/test-keyword.hg
82 pulling from *test-keyword.hg (glob)
82 pulling from *test-keyword.hg (glob)
83 requesting all changes
83 requesting all changes
84 adding changesets
84 adding changesets
85 adding manifests
85 adding manifests
86 adding file changes
86 adding file changes
87 added 1 changesets with 1 changes to 1 files
87 added 1 changesets with 1 changes to 1 files
88 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
89
89
90 $ echo 'expand $Id$' > a
90 $ echo 'expand $Id$' > a
91 $ echo 'do not process $Id:' >> a
91 $ echo 'do not process $Id:' >> a
92 $ echo 'xxx $' >> a
92 $ echo 'xxx $' >> a
93 $ echo 'ignore $Id$' > b
93 $ echo 'ignore $Id$' > b
94
94
95 Output files as they were created
95 Output files as they were created
96
96
97 $ cat a b
97 $ cat a b
98 expand $Id$
98 expand $Id$
99 do not process $Id:
99 do not process $Id:
100 xxx $
100 xxx $
101 ignore $Id$
101 ignore $Id$
102
102
103 no kwfiles
103 no kwfiles
104
104
105 $ hg kwfiles
105 $ hg kwfiles
106
106
107 untracked candidates
107 untracked candidates
108
108
109 $ hg -v kwfiles --unknown
109 $ hg -v kwfiles --unknown
110 k a
110 k a
111
111
112 Add files and check status
112 Add files and check status
113
113
114 $ hg addremove
114 $ hg addremove
115 adding a
115 adding a
116 adding b
116 adding b
117 $ hg status
117 $ hg status
118 A a
118 A a
119 A b
119 A b
120
120
121
121
122 Default keyword expansion including commit hook
122 Default keyword expansion including commit hook
123 Interrupted commit should not change state or run commit hook
123 Interrupted commit should not change state or run commit hook
124
124
125 $ hg --debug commit
125 $ hg --debug commit
126 abort: empty commit message
126 abort: empty commit message
127 [255]
127 [255]
128 $ hg status
128 $ hg status
129 A a
129 A a
130 A b
130 A b
131
131
132 Commit with several checks
132 Commit with several checks
133
133
134 $ hg --debug commit -mabsym -u 'User Name <user@example.com>'
134 $ hg --debug commit -mabsym -u 'User Name <user@example.com>'
135 a
135 a
136 b
136 b
137 overwriting a expanding keywords
137 overwriting a expanding keywords
138 running hook commit.test: cp a hooktest
138 running hook commit.test: cp a hooktest
139 committed changeset 1:ef63ca68695bc9495032c6fda1350c71e6d256e9
139 committed changeset 1:ef63ca68695bc9495032c6fda1350c71e6d256e9
140 $ hg status
140 $ hg status
141 ? hooktest
141 ? hooktest
142 $ hg debugrebuildstate
142 $ hg debugrebuildstate
143 $ hg --quiet identify
143 $ hg --quiet identify
144 ef63ca68695b
144 ef63ca68695b
145
145
146 cat files in working directory with keywords expanded
146 cat files in working directory with keywords expanded
147
147
148 $ cat a b
148 $ cat a b
149 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
149 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
150 do not process $Id:
150 do not process $Id:
151 xxx $
151 xxx $
152 ignore $Id$
152 ignore $Id$
153
153
154 hg cat files and symlink, no expansion
154 hg cat files and symlink, no expansion
155
155
156 $ hg cat sym a b && echo
156 $ hg cat sym a b && echo
157 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
157 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
158 do not process $Id:
158 do not process $Id:
159 xxx $
159 xxx $
160 ignore $Id$
160 ignore $Id$
161 a
161 a
162
162
163 Test hook execution
163 Test hook execution
164
164
165 $ diff a hooktest
165 $ diff a hooktest
166
166
167 $ cp $HGRCPATH.nohooks $HGRCPATH
167 $ cp $HGRCPATH.nohooks $HGRCPATH
168 $ rm hooktest
168 $ rm hooktest
169
169
170 bundle
170 bundle
171
171
172 $ hg bundle --base null ../kw.hg
172 $ hg bundle --base null ../kw.hg
173 2 changesets found
173 2 changesets found
174 $ cd ..
174 $ cd ..
175 $ hg init Test
175 $ hg init Test
176 $ cd Test
176 $ cd Test
177
177
178 Notify on pull to check whether keywords stay as is in email
178 Notify on pull to check whether keywords stay as is in email
179 ie. if patch.diff wrapper acts as it should
179 ie. if patch.diff wrapper acts as it should
180
180
181 $ cat <<EOF >> $HGRCPATH
181 $ cat <<EOF >> $HGRCPATH
182 > [hooks]
182 > [hooks]
183 > incoming.notify = python:hgext.notify.hook
183 > incoming.notify = python:hgext.notify.hook
184 > [notify]
184 > [notify]
185 > sources = pull
185 > sources = pull
186 > diffstat = False
186 > diffstat = False
187 > maxsubject = 15
187 > maxsubject = 15
188 > [reposubs]
188 > [reposubs]
189 > * = Test
189 > * = Test
190 > EOF
190 > EOF
191
191
192 Pull from bundle and trigger notify
192 Pull from bundle and trigger notify
193
193
194 $ hg pull -u ../kw.hg
194 $ hg pull -u ../kw.hg
195 pulling from ../kw.hg
195 pulling from ../kw.hg
196 requesting all changes
196 requesting all changes
197 adding changesets
197 adding changesets
198 adding manifests
198 adding manifests
199 adding file changes
199 adding file changes
200 added 2 changesets with 3 changes to 3 files
200 added 2 changesets with 3 changes to 3 files
201 Content-Type: text/plain; charset="us-ascii"
201 Content-Type: text/plain; charset="us-ascii"
202 MIME-Version: 1.0
202 MIME-Version: 1.0
203 Content-Transfer-Encoding: 7bit
203 Content-Transfer-Encoding: 7bit
204 Date: * (glob)
204 Date: * (glob)
205 Subject: changeset in...
205 Subject: changeset in...
206 From: mercurial
206 From: mercurial
207 X-Hg-Notification: changeset a2392c293916
207 X-Hg-Notification: changeset a2392c293916
208 Message-Id: <hg.a2392c293916*> (glob)
208 Message-Id: <hg.a2392c293916*> (glob)
209 To: Test
209 To: Test
210
210
211 changeset a2392c293916 in $TESTTMP/Test
211 changeset a2392c293916 in $TESTTMP/Test
212 details: $TESTTMP/Test?cmd=changeset;node=a2392c293916
212 details: $TESTTMP/Test?cmd=changeset;node=a2392c293916
213 description:
213 description:
214 addsym
214 addsym
215
215
216 diffs (6 lines):
216 diffs (6 lines):
217
217
218 diff -r 000000000000 -r a2392c293916 sym
218 diff -r 000000000000 -r a2392c293916 sym
219 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
219 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
220 +++ b/sym Sat Feb 09 20:25:47 2008 +0100
220 +++ b/sym Sat Feb 09 20:25:47 2008 +0100
221 @@ -0,0 +1,1 @@
221 @@ -0,0 +1,1 @@
222 +a
222 +a
223 \ No newline at end of file
223 \ No newline at end of file
224 Content-Type: text/plain; charset="us-ascii"
224 Content-Type: text/plain; charset="us-ascii"
225 MIME-Version: 1.0
225 MIME-Version: 1.0
226 Content-Transfer-Encoding: 7bit
226 Content-Transfer-Encoding: 7bit
227 Date:* (glob)
227 Date:* (glob)
228 Subject: changeset in...
228 Subject: changeset in...
229 From: User Name <user@example.com>
229 From: User Name <user@example.com>
230 X-Hg-Notification: changeset ef63ca68695b
230 X-Hg-Notification: changeset ef63ca68695b
231 Message-Id: <hg.ef63ca68695b*> (glob)
231 Message-Id: <hg.ef63ca68695b*> (glob)
232 To: Test
232 To: Test
233
233
234 changeset ef63ca68695b in $TESTTMP/Test
234 changeset ef63ca68695b in $TESTTMP/Test
235 details: $TESTTMP/Test?cmd=changeset;node=ef63ca68695b
235 details: $TESTTMP/Test?cmd=changeset;node=ef63ca68695b
236 description:
236 description:
237 absym
237 absym
238
238
239 diffs (12 lines):
239 diffs (12 lines):
240
240
241 diff -r a2392c293916 -r ef63ca68695b a
241 diff -r a2392c293916 -r ef63ca68695b a
242 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
242 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
243 +++ b/a Thu Jan 01 00:00:00 1970 +0000
243 +++ b/a Thu Jan 01 00:00:00 1970 +0000
244 @@ -0,0 +1,3 @@
244 @@ -0,0 +1,3 @@
245 +expand $Id$
245 +expand $Id$
246 +do not process $Id:
246 +do not process $Id:
247 +xxx $
247 +xxx $
248 diff -r a2392c293916 -r ef63ca68695b b
248 diff -r a2392c293916 -r ef63ca68695b b
249 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
249 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
250 +++ b/b Thu Jan 01 00:00:00 1970 +0000
250 +++ b/b Thu Jan 01 00:00:00 1970 +0000
251 @@ -0,0 +1,1 @@
251 @@ -0,0 +1,1 @@
252 +ignore $Id$
252 +ignore $Id$
253 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
253 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
254
254
255 $ cp $HGRCPATH.nohooks $HGRCPATH
255 $ cp $HGRCPATH.nohooks $HGRCPATH
256
256
257 Touch files and check with status
257 Touch files and check with status
258
258
259 $ touch a b
259 $ touch a b
260 $ hg status
260 $ hg status
261
261
262 Update and expand
262 Update and expand
263
263
264 $ rm sym a b
264 $ rm sym a b
265 $ hg update -C
265 $ hg update -C
266 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
266 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
267 $ cat a b
267 $ cat a b
268 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
268 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
269 do not process $Id:
269 do not process $Id:
270 xxx $
270 xxx $
271 ignore $Id$
271 ignore $Id$
272
272
273 Check whether expansion is filewise and file mode is preserved
273 Check whether expansion is filewise and file mode is preserved
274
274
275 $ echo '$Id$' > c
275 $ echo '$Id$' > c
276 $ echo 'tests for different changenodes' >> c
276 $ echo 'tests for different changenodes' >> c
277 $ chmod 600 c
277 $ chmod 600 c
278 $ ls -l c | cut -b 1-10
278 $ ls -l c | cut -b 1-10
279 -rw-------
279 -rw-------
280
280
281 commit file c
281 commit file c
282
282
283 $ hg commit -A -mcndiff -d '1 0' -u 'User Name <user@example.com>'
283 $ hg commit -A -mcndiff -d '1 0' -u 'User Name <user@example.com>'
284 adding c
284 adding c
285 $ ls -l c | cut -b 1-10
285 $ ls -l c | cut -b 1-10
286 -rw-------
286 -rw-------
287
287
288 force expansion
288 force expansion
289
289
290 $ hg -v kwexpand
290 $ hg -v kwexpand
291 overwriting a expanding keywords
291 overwriting a expanding keywords
292 overwriting c expanding keywords
292 overwriting c expanding keywords
293
293
294 compare changenodes in a and c
294 compare changenodes in a and c
295
295
296 $ cat a c
296 $ cat a c
297 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
297 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
298 do not process $Id:
298 do not process $Id:
299 xxx $
299 xxx $
300 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
300 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
301 tests for different changenodes
301 tests for different changenodes
302
302
303 record
303 record
304
304
305 $ echo '$Id$' > r
305 $ echo '$Id$' > r
306 $ hg add r
306 $ hg add r
307
307
308 record chunk
308 record chunk
309
309
310 $ python -c \
310 $ python -c \
311 > 'l=open("a").readlines();l.insert(1,"foo\n");l.append("bar\n");open("a","w").writelines(l);'
311 > 'l=open("a").readlines();l.insert(1,"foo\n");l.append("bar\n");open("a","w").writelines(l);'
312 $ hg record -d '1 10' -m rectest a<<EOF
312 $ hg record -d '1 10' -m rectest a<<EOF
313 > y
313 > y
314 > y
314 > y
315 > n
315 > n
316 > EOF
316 > EOF
317 diff --git a/a b/a
317 diff --git a/a b/a
318 2 hunks, 2 lines changed
318 2 hunks, 2 lines changed
319 examine changes to 'a'? [Ynsfdaq?]
319 examine changes to 'a'? [Ynsfdaq?]
320 @@ -1,3 +1,4 @@
320 @@ -1,3 +1,4 @@
321 expand $Id$
321 expand $Id$
322 +foo
322 +foo
323 do not process $Id:
323 do not process $Id:
324 xxx $
324 xxx $
325 record change 1/2 to 'a'? [Ynsfdaq?]
325 record change 1/2 to 'a'? [Ynsfdaq?]
326 @@ -2,2 +3,3 @@
326 @@ -2,2 +3,3 @@
327 do not process $Id:
327 do not process $Id:
328 xxx $
328 xxx $
329 +bar
329 +bar
330 record change 2/2 to 'a'? [Ynsfdaq?]
330 record change 2/2 to 'a'? [Ynsfdaq?]
331
331
332 $ hg identify
332 $ hg identify
333 d17e03c92c97+ tip
333 d17e03c92c97+ tip
334 $ hg status
334 $ hg status
335 M a
335 M a
336 A r
336 A r
337
337
338 Cat modified file a
338 Cat modified file a
339
339
340 $ cat a
340 $ cat a
341 expand $Id: a,v d17e03c92c97 1970/01/01 00:00:01 test $
341 expand $Id: a,v d17e03c92c97 1970/01/01 00:00:01 test $
342 foo
342 foo
343 do not process $Id:
343 do not process $Id:
344 xxx $
344 xxx $
345 bar
345 bar
346
346
347 Diff remaining chunk
347 Diff remaining chunk
348
348
349 $ hg diff a
349 $ hg diff a
350 diff -r d17e03c92c97 a
350 diff -r d17e03c92c97 a
351 --- a/a Wed Dec 31 23:59:51 1969 -0000
351 --- a/a Wed Dec 31 23:59:51 1969 -0000
352 +++ b/a * (glob)
352 +++ b/a * (glob)
353 @@ -2,3 +2,4 @@
353 @@ -2,3 +2,4 @@
354 foo
354 foo
355 do not process $Id:
355 do not process $Id:
356 xxx $
356 xxx $
357 +bar
357 +bar
358
358
359 $ hg rollback
359 $ hg rollback
360 repository tip rolled back to revision 2 (undo commit)
360 repository tip rolled back to revision 2 (undo commit)
361 working directory now based on revision 2
361 working directory now based on revision 2
362
362
363 Record all chunks in file a
363 Record all chunks in file a
364
364
365 $ echo foo > msg
365 $ echo foo > msg
366
366
367 - do not use "hg record -m" here!
367 - do not use "hg record -m" here!
368
368
369 $ hg record -l msg -d '1 11' a<<EOF
369 $ hg record -l msg -d '1 11' a<<EOF
370 > y
370 > y
371 > y
371 > y
372 > y
372 > y
373 > EOF
373 > EOF
374 diff --git a/a b/a
374 diff --git a/a b/a
375 2 hunks, 2 lines changed
375 2 hunks, 2 lines changed
376 examine changes to 'a'? [Ynsfdaq?]
376 examine changes to 'a'? [Ynsfdaq?]
377 @@ -1,3 +1,4 @@
377 @@ -1,3 +1,4 @@
378 expand $Id$
378 expand $Id$
379 +foo
379 +foo
380 do not process $Id:
380 do not process $Id:
381 xxx $
381 xxx $
382 record change 1/2 to 'a'? [Ynsfdaq?]
382 record change 1/2 to 'a'? [Ynsfdaq?]
383 @@ -2,2 +3,3 @@
383 @@ -2,2 +3,3 @@
384 do not process $Id:
384 do not process $Id:
385 xxx $
385 xxx $
386 +bar
386 +bar
387 record change 2/2 to 'a'? [Ynsfdaq?]
387 record change 2/2 to 'a'? [Ynsfdaq?]
388
388
389 File a should be clean
389 File a should be clean
390
390
391 $ hg status -A a
391 $ hg status -A a
392 C a
392 C a
393
393
394 rollback and revert expansion
394 rollback and revert expansion
395
395
396 $ cat a
396 $ cat a
397 expand $Id: a,v 59f969a3b52c 1970/01/01 00:00:01 test $
397 expand $Id: a,v 59f969a3b52c 1970/01/01 00:00:01 test $
398 foo
398 foo
399 do not process $Id:
399 do not process $Id:
400 xxx $
400 xxx $
401 bar
401 bar
402 $ hg --verbose rollback
402 $ hg --verbose rollback
403 repository tip rolled back to revision 2 (undo commit)
403 repository tip rolled back to revision 2 (undo commit)
404 working directory now based on revision 2
404 working directory now based on revision 2
405 overwriting a expanding keywords
405 overwriting a expanding keywords
406 $ hg status a
406 $ hg status a
407 M a
407 M a
408 $ cat a
408 $ cat a
409 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
409 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
410 foo
410 foo
411 do not process $Id:
411 do not process $Id:
412 xxx $
412 xxx $
413 bar
413 bar
414 $ echo '$Id$' > y
414 $ echo '$Id$' > y
415 $ echo '$Id$' > z
415 $ echo '$Id$' > z
416 $ hg add y
416 $ hg add y
417 $ hg commit -Am "rollback only" z
417 $ hg commit -Am "rollback only" z
418 $ cat z
418 $ cat z
419 $Id: z,v 45a5d3adce53 1970/01/01 00:00:00 test $
419 $Id: z,v 45a5d3adce53 1970/01/01 00:00:00 test $
420 $ hg --verbose rollback
420 $ hg --verbose rollback
421 repository tip rolled back to revision 2 (undo commit)
421 repository tip rolled back to revision 2 (undo commit)
422 working directory now based on revision 2
422 working directory now based on revision 2
423 overwriting z shrinking keywords
423 overwriting z shrinking keywords
424
424
425 Only z should be overwritten
425 Only z should be overwritten
426
426
427 $ hg status a y z
427 $ hg status a y z
428 M a
428 M a
429 A y
429 A y
430 A z
430 A z
431 $ cat z
431 $ cat z
432 $Id$
432 $Id$
433 $ hg forget y z
433 $ hg forget y z
434 $ rm y z
434 $ rm y z
435
435
436 record added file alone
436 record added file alone
437
437
438 $ hg -v record -l msg -d '1 12' r<<EOF
438 $ hg -v record -l msg -d '1 12' r<<EOF
439 > y
439 > y
440 > EOF
440 > EOF
441 diff --git a/r b/r
441 diff --git a/r b/r
442 new file mode 100644
442 new file mode 100644
443 examine changes to 'r'? [Ynsfdaq?]
443 examine changes to 'r'? [Ynsfdaq?]
444 r
444 r
445 committed changeset 3:899491280810
445 committed changeset 3:899491280810
446 overwriting r expanding keywords
446 overwriting r expanding keywords
447 - status call required for dirstate.normallookup() check
448 $ hg status r
447 $ hg --verbose rollback
449 $ hg --verbose rollback
448 repository tip rolled back to revision 2 (undo commit)
450 repository tip rolled back to revision 2 (undo commit)
449 working directory now based on revision 2
451 working directory now based on revision 2
450 overwriting r shrinking keywords
452 overwriting r shrinking keywords
451 $ hg forget r
453 $ hg forget r
452 $ rm msg r
454 $ rm msg r
453 $ hg update -C
455 $ hg update -C
454 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
456 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
455
457
456 record added keyword ignored file
458 record added keyword ignored file
457
459
458 $ echo '$Id$' > i
460 $ echo '$Id$' > i
459 $ hg add i
461 $ hg add i
460 $ hg --verbose record -d '1 13' -m recignored<<EOF
462 $ hg --verbose record -d '1 13' -m recignored<<EOF
461 > y
463 > y
462 > EOF
464 > EOF
463 diff --git a/i b/i
465 diff --git a/i b/i
464 new file mode 100644
466 new file mode 100644
465 examine changes to 'i'? [Ynsfdaq?]
467 examine changes to 'i'? [Ynsfdaq?]
466 i
468 i
467 committed changeset 3:5f40fe93bbdc
469 committed changeset 3:5f40fe93bbdc
468 $ cat i
470 $ cat i
469 $Id$
471 $Id$
470 $ hg -q rollback
472 $ hg -q rollback
471 $ hg forget i
473 $ hg forget i
472 $ rm i
474 $ rm i
473
475
474 Test patch queue repo
476 Test patch queue repo
475
477
476 $ hg init --mq
478 $ hg init --mq
477 $ hg qimport -r tip -n mqtest.diff
479 $ hg qimport -r tip -n mqtest.diff
478 $ hg commit --mq -m mqtest
480 $ hg commit --mq -m mqtest
479
481
480 Keywords should not be expanded in patch
482 Keywords should not be expanded in patch
481
483
482 $ cat .hg/patches/mqtest.diff
484 $ cat .hg/patches/mqtest.diff
483 # HG changeset patch
485 # HG changeset patch
484 # User User Name <user@example.com>
486 # User User Name <user@example.com>
485 # Date 1 0
487 # Date 1 0
486 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad
488 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad
487 # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9
489 # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9
488 cndiff
490 cndiff
489
491
490 diff -r ef63ca68695b -r 40a904bbbe4c c
492 diff -r ef63ca68695b -r 40a904bbbe4c c
491 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
493 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
492 +++ b/c Thu Jan 01 00:00:01 1970 +0000
494 +++ b/c Thu Jan 01 00:00:01 1970 +0000
493 @@ -0,0 +1,2 @@
495 @@ -0,0 +1,2 @@
494 +$Id$
496 +$Id$
495 +tests for different changenodes
497 +tests for different changenodes
496
498
497 $ hg qpop
499 $ hg qpop
498 popping mqtest.diff
500 popping mqtest.diff
499 patch queue now empty
501 patch queue now empty
500
502
501 qgoto, implying qpush, should expand
503 qgoto, implying qpush, should expand
502
504
503 $ hg qgoto mqtest.diff
505 $ hg qgoto mqtest.diff
504 applying mqtest.diff
506 applying mqtest.diff
505 now at: mqtest.diff
507 now at: mqtest.diff
506 $ cat c
508 $ cat c
507 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
509 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
508 tests for different changenodes
510 tests for different changenodes
509 $ hg cat c
511 $ hg cat c
510 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
512 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
511 tests for different changenodes
513 tests for different changenodes
512
514
513 Keywords should not be expanded in filelog
515 Keywords should not be expanded in filelog
514
516
515 $ hg --config 'extensions.keyword=!' cat c
517 $ hg --config 'extensions.keyword=!' cat c
516 $Id$
518 $Id$
517 tests for different changenodes
519 tests for different changenodes
518
520
519 qpop and move on
521 qpop and move on
520
522
521 $ hg qpop
523 $ hg qpop
522 popping mqtest.diff
524 popping mqtest.diff
523 patch queue now empty
525 patch queue now empty
524
526
525 Copy and show added kwfiles
527 Copy and show added kwfiles
526
528
527 $ hg cp a c
529 $ hg cp a c
528 $ hg kwfiles
530 $ hg kwfiles
529 a
531 a
530 c
532 c
531
533
532 Commit and show expansion in original and copy
534 Commit and show expansion in original and copy
533
535
534 $ hg --debug commit -ma2c -d '1 0' -u 'User Name <user@example.com>'
536 $ hg --debug commit -ma2c -d '1 0' -u 'User Name <user@example.com>'
535 c
537 c
536 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
538 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
537 overwriting c expanding keywords
539 overwriting c expanding keywords
538 committed changeset 2:25736cf2f5cbe41f6be4e6784ef6ecf9f3bbcc7d
540 committed changeset 2:25736cf2f5cbe41f6be4e6784ef6ecf9f3bbcc7d
539 $ cat a c
541 $ cat a c
540 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
542 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
541 do not process $Id:
543 do not process $Id:
542 xxx $
544 xxx $
543 expand $Id: c,v 25736cf2f5cb 1970/01/01 00:00:01 user $
545 expand $Id: c,v 25736cf2f5cb 1970/01/01 00:00:01 user $
544 do not process $Id:
546 do not process $Id:
545 xxx $
547 xxx $
546
548
547 Touch copied c and check its status
549 Touch copied c and check its status
548
550
549 $ touch c
551 $ touch c
550 $ hg status
552 $ hg status
551
553
552 Copy kwfile to keyword ignored file unexpanding keywords
554 Copy kwfile to keyword ignored file unexpanding keywords
553
555
554 $ hg --verbose copy a i
556 $ hg --verbose copy a i
555 copying a to i
557 copying a to i
556 overwriting i shrinking keywords
558 overwriting i shrinking keywords
557 $ head -n 1 i
559 $ head -n 1 i
558 expand $Id$
560 expand $Id$
559 $ hg forget i
561 $ hg forget i
560 $ rm i
562 $ rm i
561
563
562 Copy ignored file to ignored file: no overwriting
564 Copy ignored file to ignored file: no overwriting
563
565
564 $ hg --verbose copy b i
566 $ hg --verbose copy b i
565 copying b to i
567 copying b to i
566 $ hg forget i
568 $ hg forget i
567 $ rm i
569 $ rm i
568
570
569 cp symlink file; hg cp -A symlink file (part1)
571 cp symlink file; hg cp -A symlink file (part1)
570 - copied symlink points to kwfile: overwrite
572 - copied symlink points to kwfile: overwrite
571
573
572 $ cp sym i
574 $ cp sym i
573 $ ls -l i
575 $ ls -l i
574 -rw-r--r--* (glob)
576 -rw-r--r--* (glob)
575 $ head -1 i
577 $ head -1 i
576 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
578 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
577 $ hg copy --after --verbose sym i
579 $ hg copy --after --verbose sym i
578 copying sym to i
580 copying sym to i
579 overwriting i shrinking keywords
581 overwriting i shrinking keywords
580 $ head -1 i
582 $ head -1 i
581 expand $Id$
583 expand $Id$
582 $ hg forget i
584 $ hg forget i
583 $ rm i
585 $ rm i
584
586
585 Test different options of hg kwfiles
587 Test different options of hg kwfiles
586
588
587 $ hg kwfiles
589 $ hg kwfiles
588 a
590 a
589 c
591 c
590 $ hg -v kwfiles --ignore
592 $ hg -v kwfiles --ignore
591 I b
593 I b
592 I sym
594 I sym
593 $ hg kwfiles --all
595 $ hg kwfiles --all
594 K a
596 K a
595 K c
597 K c
596 I b
598 I b
597 I sym
599 I sym
598
600
599 Diff specific revision
601 Diff specific revision
600
602
601 $ hg diff --rev 1
603 $ hg diff --rev 1
602 diff -r ef63ca68695b c
604 diff -r ef63ca68695b c
603 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
605 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
604 +++ b/c * (glob)
606 +++ b/c * (glob)
605 @@ -0,0 +1,3 @@
607 @@ -0,0 +1,3 @@
606 +expand $Id$
608 +expand $Id$
607 +do not process $Id:
609 +do not process $Id:
608 +xxx $
610 +xxx $
609
611
610 Status after rollback:
612 Status after rollback:
611
613
612 $ hg rollback
614 $ hg rollback
613 repository tip rolled back to revision 1 (undo commit)
615 repository tip rolled back to revision 1 (undo commit)
614 working directory now based on revision 1
616 working directory now based on revision 1
615 $ hg status
617 $ hg status
616 A c
618 A c
617 $ hg update --clean
619 $ hg update --clean
618 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
620 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
619
621
620 cp symlink file; hg cp -A symlink file (part2)
622 cp symlink file; hg cp -A symlink file (part2)
621 - copied symlink points to kw ignored file: do not overwrite
623 - copied symlink points to kw ignored file: do not overwrite
622
624
623 $ cat a > i
625 $ cat a > i
624 $ ln -s i symignored
626 $ ln -s i symignored
625 $ hg commit -Am 'fake expansion in ignored and symlink' i symignored
627 $ hg commit -Am 'fake expansion in ignored and symlink' i symignored
626 $ cp symignored x
628 $ cp symignored x
627 $ hg copy --after --verbose symignored x
629 $ hg copy --after --verbose symignored x
628 copying symignored to x
630 copying symignored to x
629 $ head -n 1 x
631 $ head -n 1 x
630 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
632 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
631 $ hg forget x
633 $ hg forget x
632 $ rm x
634 $ rm x
633
635
634 $ hg rollback
636 $ hg rollback
635 repository tip rolled back to revision 1 (undo commit)
637 repository tip rolled back to revision 1 (undo commit)
636 working directory now based on revision 1
638 working directory now based on revision 1
637 $ hg update --clean
639 $ hg update --clean
638 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
640 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
639 $ rm i symignored
641 $ rm i symignored
640
642
641 Custom keywordmaps as argument to kwdemo
643 Custom keywordmaps as argument to kwdemo
642
644
643 $ hg --quiet kwdemo "Xinfo = {author}: {desc}"
645 $ hg --quiet kwdemo "Xinfo = {author}: {desc}"
644 [extensions]
646 [extensions]
645 keyword =
647 keyword =
646 [keyword]
648 [keyword]
647 ** =
649 ** =
648 b = ignore
650 b = ignore
649 demo.txt =
651 demo.txt =
650 i = ignore
652 i = ignore
651 [keywordset]
653 [keywordset]
652 svn = False
654 svn = False
653 [keywordmaps]
655 [keywordmaps]
654 Xinfo = {author}: {desc}
656 Xinfo = {author}: {desc}
655 $Xinfo: test: hg keyword configuration and expansion example $
657 $Xinfo: test: hg keyword configuration and expansion example $
656
658
657 Configure custom keywordmaps
659 Configure custom keywordmaps
658
660
659 $ cat <<EOF >>$HGRCPATH
661 $ cat <<EOF >>$HGRCPATH
660 > [keywordmaps]
662 > [keywordmaps]
661 > Id = {file} {node|short} {date|rfc822date} {author|user}
663 > Id = {file} {node|short} {date|rfc822date} {author|user}
662 > Xinfo = {author}: {desc}
664 > Xinfo = {author}: {desc}
663 > EOF
665 > EOF
664
666
665 Cat and hg cat files before custom expansion
667 Cat and hg cat files before custom expansion
666
668
667 $ cat a b
669 $ cat a b
668 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
670 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
669 do not process $Id:
671 do not process $Id:
670 xxx $
672 xxx $
671 ignore $Id$
673 ignore $Id$
672 $ hg cat sym a b && echo
674 $ hg cat sym a b && echo
673 expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $
675 expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $
674 do not process $Id:
676 do not process $Id:
675 xxx $
677 xxx $
676 ignore $Id$
678 ignore $Id$
677 a
679 a
678
680
679 Write custom keyword and prepare multiline commit message
681 Write custom keyword and prepare multiline commit message
680
682
681 $ echo '$Xinfo$' >> a
683 $ echo '$Xinfo$' >> a
682 $ cat <<EOF >> log
684 $ cat <<EOF >> log
683 > firstline
685 > firstline
684 > secondline
686 > secondline
685 > EOF
687 > EOF
686
688
687 Interrupted commit should not change state
689 Interrupted commit should not change state
688
690
689 $ hg commit
691 $ hg commit
690 abort: empty commit message
692 abort: empty commit message
691 [255]
693 [255]
692 $ hg status
694 $ hg status
693 M a
695 M a
694 ? c
696 ? c
695 ? log
697 ? log
696
698
697 Commit with multiline message and custom expansion
699 Commit with multiline message and custom expansion
698
700
699 $ hg --debug commit -l log -d '2 0' -u 'User Name <user@example.com>'
701 $ hg --debug commit -l log -d '2 0' -u 'User Name <user@example.com>'
700 a
702 a
701 overwriting a expanding keywords
703 overwriting a expanding keywords
702 committed changeset 2:bb948857c743469b22bbf51f7ec8112279ca5d83
704 committed changeset 2:bb948857c743469b22bbf51f7ec8112279ca5d83
703 $ rm log
705 $ rm log
704
706
705 Stat, verify and show custom expansion (firstline)
707 Stat, verify and show custom expansion (firstline)
706
708
707 $ hg status
709 $ hg status
708 ? c
710 ? c
709 $ hg verify
711 $ hg verify
710 checking changesets
712 checking changesets
711 checking manifests
713 checking manifests
712 crosschecking files in changesets and manifests
714 crosschecking files in changesets and manifests
713 checking files
715 checking files
714 3 files, 3 changesets, 4 total revisions
716 3 files, 3 changesets, 4 total revisions
715 $ cat a b
717 $ cat a b
716 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
718 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
717 do not process $Id:
719 do not process $Id:
718 xxx $
720 xxx $
719 $Xinfo: User Name <user@example.com>: firstline $
721 $Xinfo: User Name <user@example.com>: firstline $
720 ignore $Id$
722 ignore $Id$
721 $ hg cat sym a b && echo
723 $ hg cat sym a b && echo
722 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
724 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
723 do not process $Id:
725 do not process $Id:
724 xxx $
726 xxx $
725 $Xinfo: User Name <user@example.com>: firstline $
727 $Xinfo: User Name <user@example.com>: firstline $
726 ignore $Id$
728 ignore $Id$
727 a
729 a
728
730
729 annotate
731 annotate
730
732
731 $ hg annotate a
733 $ hg annotate a
732 1: expand $Id$
734 1: expand $Id$
733 1: do not process $Id:
735 1: do not process $Id:
734 1: xxx $
736 1: xxx $
735 2: $Xinfo$
737 2: $Xinfo$
736
738
737 remove with status checks
739 remove with status checks
738
740
739 $ hg debugrebuildstate
741 $ hg debugrebuildstate
740 $ hg remove a
742 $ hg remove a
741 $ hg --debug commit -m rma
743 $ hg --debug commit -m rma
742 committed changeset 3:d14c712653769de926994cf7fbb06c8fbd68f012
744 committed changeset 3:d14c712653769de926994cf7fbb06c8fbd68f012
743 $ hg status
745 $ hg status
744 ? c
746 ? c
745
747
746 Rollback, revert, and check expansion
748 Rollback, revert, and check expansion
747
749
748 $ hg rollback
750 $ hg rollback
749 repository tip rolled back to revision 2 (undo commit)
751 repository tip rolled back to revision 2 (undo commit)
750 working directory now based on revision 2
752 working directory now based on revision 2
751 $ hg status
753 $ hg status
752 R a
754 R a
753 ? c
755 ? c
754 $ hg revert --no-backup --rev tip a
756 $ hg revert --no-backup --rev tip a
755 $ cat a
757 $ cat a
756 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
758 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
757 do not process $Id:
759 do not process $Id:
758 xxx $
760 xxx $
759 $Xinfo: User Name <user@example.com>: firstline $
761 $Xinfo: User Name <user@example.com>: firstline $
760
762
761 Clone to test global and local configurations
763 Clone to test global and local configurations
762
764
763 $ cd ..
765 $ cd ..
764
766
765 Expansion in destinaton with global configuration
767 Expansion in destinaton with global configuration
766
768
767 $ hg --quiet clone Test globalconf
769 $ hg --quiet clone Test globalconf
768 $ cat globalconf/a
770 $ cat globalconf/a
769 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
771 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
770 do not process $Id:
772 do not process $Id:
771 xxx $
773 xxx $
772 $Xinfo: User Name <user@example.com>: firstline $
774 $Xinfo: User Name <user@example.com>: firstline $
773
775
774 No expansion in destination with local configuration in origin only
776 No expansion in destination with local configuration in origin only
775
777
776 $ hg --quiet --config 'keyword.**=ignore' clone Test localconf
778 $ hg --quiet --config 'keyword.**=ignore' clone Test localconf
777 $ cat localconf/a
779 $ cat localconf/a
778 expand $Id$
780 expand $Id$
779 do not process $Id:
781 do not process $Id:
780 xxx $
782 xxx $
781 $Xinfo$
783 $Xinfo$
782
784
783 Clone to test incoming
785 Clone to test incoming
784
786
785 $ hg clone -r1 Test Test-a
787 $ hg clone -r1 Test Test-a
786 adding changesets
788 adding changesets
787 adding manifests
789 adding manifests
788 adding file changes
790 adding file changes
789 added 2 changesets with 3 changes to 3 files
791 added 2 changesets with 3 changes to 3 files
790 updating to branch default
792 updating to branch default
791 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
793 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
792 $ cd Test-a
794 $ cd Test-a
793 $ cat <<EOF >> .hg/hgrc
795 $ cat <<EOF >> .hg/hgrc
794 > [paths]
796 > [paths]
795 > default = ../Test
797 > default = ../Test
796 > EOF
798 > EOF
797 $ hg incoming
799 $ hg incoming
798 comparing with $TESTTMP/Test
800 comparing with $TESTTMP/Test
799 searching for changes
801 searching for changes
800 changeset: 2:bb948857c743
802 changeset: 2:bb948857c743
801 tag: tip
803 tag: tip
802 user: User Name <user@example.com>
804 user: User Name <user@example.com>
803 date: Thu Jan 01 00:00:02 1970 +0000
805 date: Thu Jan 01 00:00:02 1970 +0000
804 summary: firstline
806 summary: firstline
805
807
806 Imported patch should not be rejected
808 Imported patch should not be rejected
807
809
808 $ python -c \
810 $ python -c \
809 > 'import re; s=re.sub("(Id.*)","\\1 rejecttest",open("a").read()); open("a","wb").write(s);'
811 > 'import re; s=re.sub("(Id.*)","\\1 rejecttest",open("a").read()); open("a","wb").write(s);'
810 $ hg --debug commit -m'rejects?' -d '3 0' -u 'User Name <user@example.com>'
812 $ hg --debug commit -m'rejects?' -d '3 0' -u 'User Name <user@example.com>'
811 a
813 a
812 overwriting a expanding keywords
814 overwriting a expanding keywords
813 committed changeset 2:85e279d709ffc28c9fdd1b868570985fc3d87082
815 committed changeset 2:85e279d709ffc28c9fdd1b868570985fc3d87082
814 $ hg export -o ../rejecttest.diff tip
816 $ hg export -o ../rejecttest.diff tip
815 $ cd ../Test
817 $ cd ../Test
816 $ hg import ../rejecttest.diff
818 $ hg import ../rejecttest.diff
817 applying ../rejecttest.diff
819 applying ../rejecttest.diff
818 $ cat a b
820 $ cat a b
819 expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
821 expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
820 do not process $Id: rejecttest
822 do not process $Id: rejecttest
821 xxx $
823 xxx $
822 $Xinfo: User Name <user@example.com>: rejects? $
824 $Xinfo: User Name <user@example.com>: rejects? $
823 ignore $Id$
825 ignore $Id$
824
826
825 $ hg rollback
827 $ hg rollback
826 repository tip rolled back to revision 2 (undo commit)
828 repository tip rolled back to revision 2 (undo commit)
827 working directory now based on revision 2
829 working directory now based on revision 2
828 $ hg update --clean
830 $ hg update --clean
829 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
831 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
830
832
831 kwexpand/kwshrink on selected files
833 kwexpand/kwshrink on selected files
832
834
833 $ mkdir x
835 $ mkdir x
834 $ hg copy a x/a
836 $ hg copy a x/a
835 $ hg --verbose kwshrink a
837 $ hg --verbose kwshrink a
836 overwriting a shrinking keywords
838 overwriting a shrinking keywords
839 - sleep required for dirstate.normal() check
840 $ sleep 1
837 $ hg status a
841 $ hg status a
838 $ hg --verbose kwexpand a
842 $ hg --verbose kwexpand a
839 overwriting a expanding keywords
843 overwriting a expanding keywords
840 $ hg status a
844 $ hg status a
841
845
842 kwexpand x/a should abort
846 kwexpand x/a should abort
843
847
844 $ hg --verbose kwexpand x/a
848 $ hg --verbose kwexpand x/a
845 abort: outstanding uncommitted changes
849 abort: outstanding uncommitted changes
846 [255]
850 [255]
847 $ cd x
851 $ cd x
848 $ hg --debug commit -m xa -d '3 0' -u 'User Name <user@example.com>'
852 $ hg --debug commit -m xa -d '3 0' -u 'User Name <user@example.com>'
849 x/a
853 x/a
850 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
854 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
851 overwriting x/a expanding keywords
855 overwriting x/a expanding keywords
852 committed changeset 3:b4560182a3f9a358179fd2d835c15e9da379c1e4
856 committed changeset 3:b4560182a3f9a358179fd2d835c15e9da379c1e4
853 $ cat a
857 $ cat a
854 expand $Id: x/a b4560182a3f9 Thu, 01 Jan 1970 00:00:03 +0000 user $
858 expand $Id: x/a b4560182a3f9 Thu, 01 Jan 1970 00:00:03 +0000 user $
855 do not process $Id:
859 do not process $Id:
856 xxx $
860 xxx $
857 $Xinfo: User Name <user@example.com>: xa $
861 $Xinfo: User Name <user@example.com>: xa $
858
862
859 kwshrink a inside directory x
863 kwshrink a inside directory x
860
864
861 $ hg --verbose kwshrink a
865 $ hg --verbose kwshrink a
862 overwriting x/a shrinking keywords
866 overwriting x/a shrinking keywords
863 $ cat a
867 $ cat a
864 expand $Id$
868 expand $Id$
865 do not process $Id:
869 do not process $Id:
866 xxx $
870 xxx $
867 $Xinfo$
871 $Xinfo$
868 $ cd ..
872 $ cd ..
869
873
870 kwexpand nonexistent
874 kwexpand nonexistent
871
875
872 $ hg kwexpand nonexistent
876 $ hg kwexpand nonexistent
873 nonexistent:* (glob)
877 nonexistent:* (glob)
874
878
875
879
876 hg serve
880 hg serve
877 - expand with hgweb file
881 - expand with hgweb file
878 - no expansion with hgweb annotate/changeset/filediff
882 - no expansion with hgweb annotate/changeset/filediff
879 - check errors
883 - check errors
880
884
881 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
885 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
882 $ cat hg.pid >> $DAEMON_PIDS
886 $ cat hg.pid >> $DAEMON_PIDS
883 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/file/tip/a/?style=raw'
887 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/file/tip/a/?style=raw'
884 200 Script output follows
888 200 Script output follows
885
889
886 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
890 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
887 do not process $Id:
891 do not process $Id:
888 xxx $
892 xxx $
889 $Xinfo: User Name <user@example.com>: firstline $
893 $Xinfo: User Name <user@example.com>: firstline $
890 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/annotate/tip/a/?style=raw'
894 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/annotate/tip/a/?style=raw'
891 200 Script output follows
895 200 Script output follows
892
896
893
897
894 user@1: expand $Id$
898 user@1: expand $Id$
895 user@1: do not process $Id:
899 user@1: do not process $Id:
896 user@1: xxx $
900 user@1: xxx $
897 user@2: $Xinfo$
901 user@2: $Xinfo$
898
902
899
903
900
904
901
905
902 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/rev/tip/?style=raw'
906 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/rev/tip/?style=raw'
903 200 Script output follows
907 200 Script output follows
904
908
905
909
906 # HG changeset patch
910 # HG changeset patch
907 # User User Name <user@example.com>
911 # User User Name <user@example.com>
908 # Date 3 0
912 # Date 3 0
909 # Node ID b4560182a3f9a358179fd2d835c15e9da379c1e4
913 # Node ID b4560182a3f9a358179fd2d835c15e9da379c1e4
910 # Parent bb948857c743469b22bbf51f7ec8112279ca5d83
914 # Parent bb948857c743469b22bbf51f7ec8112279ca5d83
911 xa
915 xa
912
916
913 diff -r bb948857c743 -r b4560182a3f9 x/a
917 diff -r bb948857c743 -r b4560182a3f9 x/a
914 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
918 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
915 +++ b/x/a Thu Jan 01 00:00:03 1970 +0000
919 +++ b/x/a Thu Jan 01 00:00:03 1970 +0000
916 @@ -0,0 +1,4 @@
920 @@ -0,0 +1,4 @@
917 +expand $Id$
921 +expand $Id$
918 +do not process $Id:
922 +do not process $Id:
919 +xxx $
923 +xxx $
920 +$Xinfo$
924 +$Xinfo$
921
925
922 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/diff/bb948857c743/a?style=raw'
926 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/diff/bb948857c743/a?style=raw'
923 200 Script output follows
927 200 Script output follows
924
928
925
929
926 diff -r ef63ca68695b -r bb948857c743 a
930 diff -r ef63ca68695b -r bb948857c743 a
927 --- a/a Thu Jan 01 00:00:00 1970 +0000
931 --- a/a Thu Jan 01 00:00:00 1970 +0000
928 +++ b/a Thu Jan 01 00:00:02 1970 +0000
932 +++ b/a Thu Jan 01 00:00:02 1970 +0000
929 @@ -1,3 +1,4 @@
933 @@ -1,3 +1,4 @@
930 expand $Id$
934 expand $Id$
931 do not process $Id:
935 do not process $Id:
932 xxx $
936 xxx $
933 +$Xinfo$
937 +$Xinfo$
934
938
935
939
936
940
937
941
938 $ cat errors.log
942 $ cat errors.log
939
943
940 Prepare merge and resolve tests
944 Prepare merge and resolve tests
941
945
942 $ echo '$Id$' > m
946 $ echo '$Id$' > m
943 $ hg add m
947 $ hg add m
944 $ hg commit -m 4kw
948 $ hg commit -m 4kw
945 $ echo foo >> m
949 $ echo foo >> m
946 $ hg commit -m 5foo
950 $ hg commit -m 5foo
947
951
948 simplemerge
952 simplemerge
949
953
950 $ hg update 4
954 $ hg update 4
951 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
955 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
952 $ echo foo >> m
956 $ echo foo >> m
953 $ hg commit -m 6foo
957 $ hg commit -m 6foo
954 created new head
958 created new head
955 $ hg merge
959 $ hg merge
956 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
960 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
957 (branch merge, don't forget to commit)
961 (branch merge, don't forget to commit)
958 $ hg commit -m simplemerge
962 $ hg commit -m simplemerge
959 $ cat m
963 $ cat m
960 $Id: m 27d48ee14f67 Thu, 01 Jan 1970 00:00:00 +0000 test $
964 $Id: m 27d48ee14f67 Thu, 01 Jan 1970 00:00:00 +0000 test $
961 foo
965 foo
962
966
963 conflict: keyword should stay outside conflict zone
967 conflict: keyword should stay outside conflict zone
964
968
965 $ hg update 4
969 $ hg update 4
966 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
970 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
967 $ echo bar >> m
971 $ echo bar >> m
968 $ hg commit -m 8bar
972 $ hg commit -m 8bar
969 created new head
973 created new head
970 $ hg merge
974 $ hg merge
971 merging m
975 merging m
972 warning: conflicts during merge.
976 warning: conflicts during merge.
973 merging m failed!
977 merging m failed!
974 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
978 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
975 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
979 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
976 [1]
980 [1]
977 $ cat m
981 $ cat m
978 $Id$
982 $Id$
979 <<<<<<< local
983 <<<<<<< local
980 bar
984 bar
981 =======
985 =======
982 foo
986 foo
983 >>>>>>> other
987 >>>>>>> other
984
988
985 resolve to local
989 resolve to local
986
990
987 $ HGMERGE=internal:local hg resolve -a
991 $ HGMERGE=internal:local hg resolve -a
988 $ hg commit -m localresolve
992 $ hg commit -m localresolve
989 $ cat m
993 $ cat m
990 $Id: m 800511b3a22d Thu, 01 Jan 1970 00:00:00 +0000 test $
994 $Id: m 800511b3a22d Thu, 01 Jan 1970 00:00:00 +0000 test $
991 bar
995 bar
992
996
993 Test restricted mode with transplant -b
997 Test restricted mode with transplant -b
994
998
995 $ hg update 6
999 $ hg update 6
996 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1000 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
997 $ hg branch foo
1001 $ hg branch foo
998 marked working directory as branch foo
1002 marked working directory as branch foo
999 $ mv a a.bak
1003 $ mv a a.bak
1000 $ echo foobranch > a
1004 $ echo foobranch > a
1001 $ cat a.bak >> a
1005 $ cat a.bak >> a
1002 $ rm a.bak
1006 $ rm a.bak
1003 $ hg commit -m 9foobranch
1007 $ hg commit -m 9foobranch
1004 $ hg update default
1008 $ hg update default
1005 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1009 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1006 $ hg -y transplant -b foo tip
1010 $ hg -y transplant -b foo tip
1007 applying 4aa30d025d50
1011 applying 4aa30d025d50
1008 4aa30d025d50 transplanted to e00abbf63521
1012 4aa30d025d50 transplanted to e00abbf63521
1009
1013
1010 Expansion in changeset but not in file
1014 Expansion in changeset but not in file
1011
1015
1012 $ hg tip -p
1016 $ hg tip -p
1013 changeset: 11:e00abbf63521
1017 changeset: 11:e00abbf63521
1014 tag: tip
1018 tag: tip
1015 parent: 9:800511b3a22d
1019 parent: 9:800511b3a22d
1016 user: test
1020 user: test
1017 date: Thu Jan 01 00:00:00 1970 +0000
1021 date: Thu Jan 01 00:00:00 1970 +0000
1018 summary: 9foobranch
1022 summary: 9foobranch
1019
1023
1020 diff -r 800511b3a22d -r e00abbf63521 a
1024 diff -r 800511b3a22d -r e00abbf63521 a
1021 --- a/a Thu Jan 01 00:00:00 1970 +0000
1025 --- a/a Thu Jan 01 00:00:00 1970 +0000
1022 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1026 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1023 @@ -1,3 +1,4 @@
1027 @@ -1,3 +1,4 @@
1024 +foobranch
1028 +foobranch
1025 expand $Id$
1029 expand $Id$
1026 do not process $Id:
1030 do not process $Id:
1027 xxx $
1031 xxx $
1028
1032
1029 $ head -n 2 a
1033 $ head -n 2 a
1030 foobranch
1034 foobranch
1031 expand $Id: a e00abbf63521 Thu, 01 Jan 1970 00:00:00 +0000 test $
1035 expand $Id: a e00abbf63521 Thu, 01 Jan 1970 00:00:00 +0000 test $
1032
1036
1033 Turn off expansion
1037 Turn off expansion
1034
1038
1035 $ hg -q rollback
1039 $ hg -q rollback
1036 $ hg -q update -C
1040 $ hg -q update -C
1037
1041
1038 kwshrink with unknown file u
1042 kwshrink with unknown file u
1039
1043
1040 $ cp a u
1044 $ cp a u
1041 $ hg --verbose kwshrink
1045 $ hg --verbose kwshrink
1042 overwriting a shrinking keywords
1046 overwriting a shrinking keywords
1043 overwriting m shrinking keywords
1047 overwriting m shrinking keywords
1044 overwriting x/a shrinking keywords
1048 overwriting x/a shrinking keywords
1045
1049
1046 Keywords shrunk in working directory, but not yet disabled
1050 Keywords shrunk in working directory, but not yet disabled
1047 - cat shows unexpanded keywords
1051 - cat shows unexpanded keywords
1048 - hg cat shows expanded keywords
1052 - hg cat shows expanded keywords
1049
1053
1050 $ cat a b
1054 $ cat a b
1051 expand $Id$
1055 expand $Id$
1052 do not process $Id:
1056 do not process $Id:
1053 xxx $
1057 xxx $
1054 $Xinfo$
1058 $Xinfo$
1055 ignore $Id$
1059 ignore $Id$
1056 $ hg cat sym a b && echo
1060 $ hg cat sym a b && echo
1057 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
1061 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
1058 do not process $Id:
1062 do not process $Id:
1059 xxx $
1063 xxx $
1060 $Xinfo: User Name <user@example.com>: firstline $
1064 $Xinfo: User Name <user@example.com>: firstline $
1061 ignore $Id$
1065 ignore $Id$
1062 a
1066 a
1063
1067
1064 Now disable keyword expansion
1068 Now disable keyword expansion
1065
1069
1066 $ rm "$HGRCPATH"
1070 $ rm "$HGRCPATH"
1067 $ cat a b
1071 $ cat a b
1068 expand $Id$
1072 expand $Id$
1069 do not process $Id:
1073 do not process $Id:
1070 xxx $
1074 xxx $
1071 $Xinfo$
1075 $Xinfo$
1072 ignore $Id$
1076 ignore $Id$
1073 $ hg cat sym a b && echo
1077 $ hg cat sym a b && echo
1074 expand $Id$
1078 expand $Id$
1075 do not process $Id:
1079 do not process $Id:
1076 xxx $
1080 xxx $
1077 $Xinfo$
1081 $Xinfo$
1078 ignore $Id$
1082 ignore $Id$
1079 a
1083 a
General Comments 0
You need to be logged in to leave comments. Login now